Scripts: OB recovery (#2964)
* Scripts: OB recovery * Scripts: slightly different ob * Scripts: remove excessive return * Scripts: simplifying work with registers * Make PVS happy Co-authored-by: SG <who.just.the.doctor@gmail.com>
This commit is contained in:
parent
c976ff11bf
commit
2702c00ba4
@ -500,7 +500,7 @@ bool furi_hal_crypto_ctr(
|
|||||||
size_t length) {
|
size_t length) {
|
||||||
/* prepare IV and counter */
|
/* prepare IV and counter */
|
||||||
uint8_t iv_and_counter[CRYPTO_CTR_IV_LEN + CRYPTO_CTR_CTR_LEN];
|
uint8_t iv_and_counter[CRYPTO_CTR_IV_LEN + CRYPTO_CTR_CTR_LEN];
|
||||||
memcpy(iv_and_counter, iv, CRYPTO_CTR_IV_LEN);
|
memcpy(iv_and_counter, iv, CRYPTO_CTR_IV_LEN); //-V1086
|
||||||
furi_hal_crypto_ctr_prep_iv(iv_and_counter);
|
furi_hal_crypto_ctr_prep_iv(iv_and_counter);
|
||||||
|
|
||||||
/* load key and IV and set the mode to CTR */
|
/* load key and IV and set the mode to CTR */
|
||||||
@ -648,7 +648,7 @@ bool furi_hal_crypto_gcm(
|
|||||||
|
|
||||||
/* prepare IV and counter */
|
/* prepare IV and counter */
|
||||||
uint8_t iv_and_counter[CRYPTO_GCM_IV_LEN + CRYPTO_GCM_CTR_LEN];
|
uint8_t iv_and_counter[CRYPTO_GCM_IV_LEN + CRYPTO_GCM_CTR_LEN];
|
||||||
memcpy(iv_and_counter, iv, CRYPTO_GCM_IV_LEN);
|
memcpy(iv_and_counter, iv, CRYPTO_GCM_IV_LEN); //-V1086
|
||||||
furi_hal_crypto_gcm_prep_iv(iv_and_counter);
|
furi_hal_crypto_gcm_prep_iv(iv_and_counter);
|
||||||
|
|
||||||
/* load key and IV and set the mode to CTR */
|
/* load key and IV and set the mode to CTR */
|
||||||
|
|||||||
@ -26,6 +26,10 @@ class Programmer(ABC):
|
|||||||
def option_bytes_set(self, file_path: str) -> bool:
|
def option_bytes_set(self, file_path: str) -> bool:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def option_bytes_recover(self) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def otp_write(self, address: int, file_path: str) -> bool:
|
def otp_write(self, address: int, file_path: str) -> bool:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@ -44,11 +44,11 @@ class OpenOCDProgrammer(Programmer):
|
|||||||
self.logger = logging.getLogger()
|
self.logger = logging.getLogger()
|
||||||
|
|
||||||
def reset(self, mode: Programmer.RunMode = Programmer.RunMode.Run) -> bool:
|
def reset(self, mode: Programmer.RunMode = Programmer.RunMode.Run) -> bool:
|
||||||
stm32 = STM32WB55()
|
stm32 = STM32WB55(self.openocd)
|
||||||
if mode == Programmer.RunMode.Run:
|
if mode == Programmer.RunMode.Run:
|
||||||
stm32.reset(self.openocd, stm32.RunMode.Run)
|
stm32.reset(stm32.RunMode.Run)
|
||||||
elif mode == Programmer.RunMode.Stop:
|
elif mode == Programmer.RunMode.Stop:
|
||||||
stm32.reset(self.openocd, stm32.RunMode.Init)
|
stm32.reset(stm32.RunMode.Init)
|
||||||
else:
|
else:
|
||||||
raise Exception("Unknown mode")
|
raise Exception("Unknown mode")
|
||||||
|
|
||||||
@ -96,11 +96,11 @@ class OpenOCDProgrammer(Programmer):
|
|||||||
|
|
||||||
def option_bytes_validate(self, file_path: str) -> bool:
|
def option_bytes_validate(self, file_path: str) -> bool:
|
||||||
# Registers
|
# Registers
|
||||||
stm32 = STM32WB55()
|
stm32 = STM32WB55(self.openocd)
|
||||||
|
|
||||||
# OpenOCD
|
# OpenOCD
|
||||||
self.openocd.start()
|
self.openocd.start()
|
||||||
stm32.reset(self.openocd, stm32.RunMode.Init)
|
stm32.reset(stm32.RunMode.Init)
|
||||||
|
|
||||||
# Generate Option Bytes data
|
# Generate Option Bytes data
|
||||||
ob_data = OptionBytesData(file_path)
|
ob_data = OptionBytesData(file_path)
|
||||||
@ -133,7 +133,7 @@ class OpenOCDProgrammer(Programmer):
|
|||||||
self._ob_print_diff_table(ob_reference, ob_compare, self.logger.error)
|
self._ob_print_diff_table(ob_reference, ob_compare, self.logger.error)
|
||||||
|
|
||||||
# Stop OpenOCD
|
# Stop OpenOCD
|
||||||
stm32.reset(self.openocd, stm32.RunMode.Run)
|
stm32.reset(stm32.RunMode.Run)
|
||||||
self.openocd.stop()
|
self.openocd.stop()
|
||||||
|
|
||||||
return return_code
|
return return_code
|
||||||
@ -143,11 +143,11 @@ class OpenOCDProgrammer(Programmer):
|
|||||||
|
|
||||||
def option_bytes_set(self, file_path: str) -> bool:
|
def option_bytes_set(self, file_path: str) -> bool:
|
||||||
# Registers
|
# Registers
|
||||||
stm32 = STM32WB55()
|
stm32 = STM32WB55(self.openocd)
|
||||||
|
|
||||||
# OpenOCD
|
# OpenOCD
|
||||||
self.openocd.start()
|
self.openocd.start()
|
||||||
stm32.reset(self.openocd, stm32.RunMode.Init)
|
stm32.reset(stm32.RunMode.Init)
|
||||||
|
|
||||||
# Generate Option Bytes data
|
# Generate Option Bytes data
|
||||||
ob_data = OptionBytesData(file_path)
|
ob_data = OptionBytesData(file_path)
|
||||||
@ -159,11 +159,11 @@ class OpenOCDProgrammer(Programmer):
|
|||||||
ob_dwords = int(ob_length / 8)
|
ob_dwords = int(ob_length / 8)
|
||||||
|
|
||||||
# Clear flash errors
|
# Clear flash errors
|
||||||
stm32.clear_flash_errors(self.openocd)
|
stm32.clear_flash_errors()
|
||||||
|
|
||||||
# Unlock Flash and Option Bytes
|
# Unlock Flash and Option Bytes
|
||||||
stm32.flash_unlock(self.openocd)
|
stm32.flash_unlock()
|
||||||
stm32.option_bytes_unlock(self.openocd)
|
stm32.option_bytes_unlock()
|
||||||
|
|
||||||
ob_need_to_apply = False
|
ob_need_to_apply = False
|
||||||
|
|
||||||
@ -194,16 +194,16 @@ class OpenOCDProgrammer(Programmer):
|
|||||||
self.openocd.write_32(device_reg_addr, ob_value)
|
self.openocd.write_32(device_reg_addr, ob_value)
|
||||||
|
|
||||||
if ob_need_to_apply:
|
if ob_need_to_apply:
|
||||||
stm32.option_bytes_apply(self.openocd)
|
stm32.option_bytes_apply()
|
||||||
else:
|
else:
|
||||||
self.logger.info("Option Bytes are already correct")
|
self.logger.info("Option Bytes are already correct")
|
||||||
|
|
||||||
# Load Option Bytes
|
# Load Option Bytes
|
||||||
# That will reset and also lock the Option Bytes and the Flash
|
# That will reset and also lock the Option Bytes and the Flash
|
||||||
stm32.option_bytes_load(self.openocd)
|
stm32.option_bytes_load()
|
||||||
|
|
||||||
# Stop OpenOCD
|
# Stop OpenOCD
|
||||||
stm32.reset(self.openocd, stm32.RunMode.Run)
|
stm32.reset(stm32.RunMode.Run)
|
||||||
self.openocd.stop()
|
self.openocd.stop()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -233,11 +233,10 @@ class OpenOCDProgrammer(Programmer):
|
|||||||
self.logger.debug(f"Data: {data.hex().upper()}")
|
self.logger.debug(f"Data: {data.hex().upper()}")
|
||||||
|
|
||||||
# Start OpenOCD
|
# Start OpenOCD
|
||||||
oocd = self.openocd
|
self.openocd.start()
|
||||||
oocd.start()
|
|
||||||
|
|
||||||
# Registers
|
# Registers
|
||||||
stm32 = STM32WB55()
|
stm32 = STM32WB55(self.openocd)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Check that OTP is empty for the given address
|
# Check that OTP is empty for the given address
|
||||||
@ -245,7 +244,7 @@ class OpenOCDProgrammer(Programmer):
|
|||||||
already_written = True
|
already_written = True
|
||||||
for i in range(0, data_size, 4):
|
for i in range(0, data_size, 4):
|
||||||
file_word = int.from_bytes(data[i : i + 4], "little")
|
file_word = int.from_bytes(data[i : i + 4], "little")
|
||||||
device_word = oocd.read_32(address + i)
|
device_word = self.openocd.read_32(address + i)
|
||||||
if device_word != 0xFFFFFFFF and device_word != file_word:
|
if device_word != 0xFFFFFFFF and device_word != file_word:
|
||||||
self.logger.error(
|
self.logger.error(
|
||||||
f"OTP memory at {address + i:08X} is not empty: {device_word:08X}"
|
f"OTP memory at {address + i:08X} is not empty: {device_word:08X}"
|
||||||
@ -260,7 +259,7 @@ class OpenOCDProgrammer(Programmer):
|
|||||||
return OpenOCDProgrammerResult.Success
|
return OpenOCDProgrammerResult.Success
|
||||||
|
|
||||||
self.reset(self.RunMode.Stop)
|
self.reset(self.RunMode.Stop)
|
||||||
stm32.clear_flash_errors(oocd)
|
stm32.clear_flash_errors()
|
||||||
|
|
||||||
# Write OTP memory by 8 bytes
|
# Write OTP memory by 8 bytes
|
||||||
for i in range(0, data_size, 8):
|
for i in range(0, data_size, 8):
|
||||||
@ -269,14 +268,14 @@ class OpenOCDProgrammer(Programmer):
|
|||||||
self.logger.debug(
|
self.logger.debug(
|
||||||
f"Writing {word_1:08X} {word_2:08X} to {address + i:08X}"
|
f"Writing {word_1:08X} {word_2:08X} to {address + i:08X}"
|
||||||
)
|
)
|
||||||
stm32.write_flash_64(oocd, address + i, word_1, word_2)
|
stm32.write_flash_64(address + i, word_1, word_2)
|
||||||
|
|
||||||
# Validate OTP memory
|
# Validate OTP memory
|
||||||
validation_result = True
|
validation_result = True
|
||||||
|
|
||||||
for i in range(0, data_size, 4):
|
for i in range(0, data_size, 4):
|
||||||
file_word = int.from_bytes(data[i : i + 4], "little")
|
file_word = int.from_bytes(data[i : i + 4], "little")
|
||||||
device_word = oocd.read_32(address + i)
|
device_word = self.openocd.read_32(address + i)
|
||||||
if file_word != device_word:
|
if file_word != device_word:
|
||||||
self.logger.error(
|
self.logger.error(
|
||||||
f"Validation failed: {file_word:08X} != {device_word:08X} at {address + i:08X}"
|
f"Validation failed: {file_word:08X} != {device_word:08X} at {address + i:08X}"
|
||||||
@ -284,11 +283,21 @@ class OpenOCDProgrammer(Programmer):
|
|||||||
validation_result = False
|
validation_result = False
|
||||||
finally:
|
finally:
|
||||||
# Stop OpenOCD
|
# Stop OpenOCD
|
||||||
stm32.reset(oocd, stm32.RunMode.Run)
|
stm32.reset(stm32.RunMode.Run)
|
||||||
oocd.stop()
|
self.openocd.stop()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
OpenOCDProgrammerResult.Success
|
OpenOCDProgrammerResult.Success
|
||||||
if validation_result
|
if validation_result
|
||||||
else OpenOCDProgrammerResult.ErrorValidation
|
else OpenOCDProgrammerResult.ErrorValidation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def option_bytes_recover(self) -> bool:
|
||||||
|
try:
|
||||||
|
self.openocd.start()
|
||||||
|
stm32 = STM32WB55(self.openocd)
|
||||||
|
stm32.reset(stm32.RunMode.Halt)
|
||||||
|
stm32.option_bytes_recover()
|
||||||
|
return True
|
||||||
|
finally:
|
||||||
|
self.openocd.stop()
|
||||||
|
|||||||
@ -16,6 +16,7 @@ class Register32:
|
|||||||
self.names = [definition.name for definition in definition_list] # typecheck
|
self.names = [definition.name for definition in definition_list] # typecheck
|
||||||
self.address = address
|
self.address = address
|
||||||
self.definition_list = definition_list
|
self.definition_list = definition_list
|
||||||
|
self.openocd = None
|
||||||
|
|
||||||
# Validate that the definitions are not overlapping
|
# Validate that the definitions are not overlapping
|
||||||
for i in range(len(definition_list)):
|
for i in range(len(definition_list)):
|
||||||
@ -76,6 +77,14 @@ class Register32:
|
|||||||
def __dir__(self):
|
def __dir__(self):
|
||||||
return self.names
|
return self.names
|
||||||
|
|
||||||
|
def set_openocd(self, openocd: OpenOCD):
|
||||||
|
self.openocd = openocd
|
||||||
|
|
||||||
|
def get_openocd(self) -> OpenOCD:
|
||||||
|
if self.openocd is None:
|
||||||
|
raise RuntimeError("OpenOCD is not installed")
|
||||||
|
return self.openocd
|
||||||
|
|
||||||
def set(self, value: int):
|
def set(self, value: int):
|
||||||
for definition in self.definition_list:
|
for definition in self.definition_list:
|
||||||
definition.value = (value >> definition.offset) & (
|
definition.value = (value >> definition.offset) & (
|
||||||
@ -88,8 +97,8 @@ class Register32:
|
|||||||
value |= definition.value << definition.offset
|
value |= definition.value << definition.offset
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def load(self, openocd: OpenOCD):
|
def load(self):
|
||||||
self.set(openocd.read_32(self.address))
|
self.set(self.get_openocd().read_32(self.address))
|
||||||
|
|
||||||
def store(self, openocd: OpenOCD):
|
def store(self):
|
||||||
openocd.write_32(self.address, self.get())
|
self.get_openocd().write_32(self.address, self.get())
|
||||||
|
|||||||
@ -108,23 +108,27 @@ class STM32WB55:
|
|||||||
15: None, # Core 2 Options
|
15: None, # Core 2 Options
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, openocd: OpenOCD):
|
||||||
|
self.openocd = openocd
|
||||||
self.logger = logging.getLogger("STM32WB55")
|
self.logger = logging.getLogger("STM32WB55")
|
||||||
|
|
||||||
|
self.FLASH_CR.set_openocd(self.openocd)
|
||||||
|
self.FLASH_SR.set_openocd(self.openocd)
|
||||||
|
|
||||||
class RunMode(Enum):
|
class RunMode(Enum):
|
||||||
Init = "init"
|
Init = "init"
|
||||||
Run = "run"
|
Run = "run"
|
||||||
Halt = "halt"
|
Halt = "halt"
|
||||||
|
|
||||||
def reset(self, oocd: OpenOCD, mode: RunMode):
|
def reset(self, mode: RunMode):
|
||||||
self.logger.debug("Resetting device")
|
self.logger.debug("Resetting device")
|
||||||
oocd.send_tcl(f"reset {mode.value}")
|
self.openocd.send_tcl(f"reset {mode.value}")
|
||||||
|
|
||||||
def clear_flash_errors(self, oocd: OpenOCD):
|
def clear_flash_errors(self):
|
||||||
# Errata 2.2.9: Flash OPTVERR flag is always set after system reset
|
# Errata 2.2.9: Flash OPTVERR flag is always set after system reset
|
||||||
# And also clear all other flash error flags
|
# And also clear all other flash error flags
|
||||||
self.logger.debug("Resetting flash errors")
|
self.logger.debug("Resetting flash errors")
|
||||||
self.FLASH_SR.load(oocd)
|
self.FLASH_SR.load()
|
||||||
self.FLASH_SR.OP_ERR = 1
|
self.FLASH_SR.OP_ERR = 1
|
||||||
self.FLASH_SR.PROG_ERR = 1
|
self.FLASH_SR.PROG_ERR = 1
|
||||||
self.FLASH_SR.WRP_ERR = 1
|
self.FLASH_SR.WRP_ERR = 1
|
||||||
@ -135,51 +139,51 @@ class STM32WB55:
|
|||||||
self.FLASH_SR.FAST_ERR = 1
|
self.FLASH_SR.FAST_ERR = 1
|
||||||
self.FLASH_SR.RD_ERR = 1
|
self.FLASH_SR.RD_ERR = 1
|
||||||
self.FLASH_SR.OPTV_ERR = 1
|
self.FLASH_SR.OPTV_ERR = 1
|
||||||
self.FLASH_SR.store(oocd)
|
self.FLASH_SR.store()
|
||||||
|
|
||||||
def flash_unlock(self, oocd: OpenOCD):
|
def flash_unlock(self):
|
||||||
# Check if flash is already unlocked
|
# Check if flash is already unlocked
|
||||||
self.FLASH_CR.load(oocd)
|
self.FLASH_CR.load()
|
||||||
if self.FLASH_CR.LOCK == 0:
|
if self.FLASH_CR.LOCK == 0:
|
||||||
self.logger.debug("Flash is already unlocked")
|
self.logger.debug("Flash is already unlocked")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Unlock flash
|
# Unlock flash
|
||||||
self.logger.debug("Unlocking Flash")
|
self.logger.debug("Unlocking Flash")
|
||||||
oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1)
|
self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1)
|
||||||
oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2)
|
self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2)
|
||||||
|
|
||||||
# Check if flash is unlocked
|
# Check if flash is unlocked
|
||||||
self.FLASH_CR.load(oocd)
|
self.FLASH_CR.load()
|
||||||
if self.FLASH_CR.LOCK == 0:
|
if self.FLASH_CR.LOCK == 0:
|
||||||
self.logger.debug("Flash unlocked")
|
self.logger.debug("Flash unlocked")
|
||||||
else:
|
else:
|
||||||
self.logger.error("Flash unlock failed")
|
self.logger.error("Flash unlock failed")
|
||||||
raise Exception("Flash unlock failed")
|
raise Exception("Flash unlock failed")
|
||||||
|
|
||||||
def option_bytes_unlock(self, oocd: OpenOCD):
|
def option_bytes_unlock(self):
|
||||||
# Check if options is already unlocked
|
# Check if options is already unlocked
|
||||||
self.FLASH_CR.load(oocd)
|
self.FLASH_CR.load()
|
||||||
if self.FLASH_CR.OPT_LOCK == 0:
|
if self.FLASH_CR.OPT_LOCK == 0:
|
||||||
self.logger.debug("Options is already unlocked")
|
self.logger.debug("Options is already unlocked")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Unlock options
|
# Unlock options
|
||||||
self.logger.debug("Unlocking Options")
|
self.logger.debug("Unlocking Options")
|
||||||
oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1)
|
self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1)
|
||||||
oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2)
|
self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2)
|
||||||
|
|
||||||
# Check if options is unlocked
|
# Check if options is unlocked
|
||||||
self.FLASH_CR.load(oocd)
|
self.FLASH_CR.load()
|
||||||
if self.FLASH_CR.OPT_LOCK == 0:
|
if self.FLASH_CR.OPT_LOCK == 0:
|
||||||
self.logger.debug("Options unlocked")
|
self.logger.debug("Options unlocked")
|
||||||
else:
|
else:
|
||||||
self.logger.error("Options unlock failed")
|
self.logger.error("Options unlock failed")
|
||||||
raise Exception("Options unlock failed")
|
raise Exception("Options unlock failed")
|
||||||
|
|
||||||
def option_bytes_lock(self, oocd: OpenOCD):
|
def option_bytes_lock(self):
|
||||||
# Check if options is already locked
|
# Check if options is already locked
|
||||||
self.FLASH_CR.load(oocd)
|
self.FLASH_CR.load()
|
||||||
if self.FLASH_CR.OPT_LOCK == 1:
|
if self.FLASH_CR.OPT_LOCK == 1:
|
||||||
self.logger.debug("Options is already locked")
|
self.logger.debug("Options is already locked")
|
||||||
return
|
return
|
||||||
@ -187,19 +191,19 @@ class STM32WB55:
|
|||||||
# Lock options
|
# Lock options
|
||||||
self.logger.debug("Locking Options")
|
self.logger.debug("Locking Options")
|
||||||
self.FLASH_CR.OPT_LOCK = 1
|
self.FLASH_CR.OPT_LOCK = 1
|
||||||
self.FLASH_CR.store(oocd)
|
self.FLASH_CR.store()
|
||||||
|
|
||||||
# Check if options is locked
|
# Check if options is locked
|
||||||
self.FLASH_CR.load(oocd)
|
self.FLASH_CR.load()
|
||||||
if self.FLASH_CR.OPT_LOCK == 1:
|
if self.FLASH_CR.OPT_LOCK == 1:
|
||||||
self.logger.debug("Options locked")
|
self.logger.debug("Options locked")
|
||||||
else:
|
else:
|
||||||
self.logger.error("Options lock failed")
|
self.logger.error("Options lock failed")
|
||||||
raise Exception("Options lock failed")
|
raise Exception("Options lock failed")
|
||||||
|
|
||||||
def flash_lock(self, oocd: OpenOCD):
|
def flash_lock(self):
|
||||||
# Check if flash is already locked
|
# Check if flash is already locked
|
||||||
self.FLASH_CR.load(oocd)
|
self.FLASH_CR.load()
|
||||||
if self.FLASH_CR.LOCK == 1:
|
if self.FLASH_CR.LOCK == 1:
|
||||||
self.logger.debug("Flash is already locked")
|
self.logger.debug("Flash is already locked")
|
||||||
return
|
return
|
||||||
@ -207,31 +211,31 @@ class STM32WB55:
|
|||||||
# Lock flash
|
# Lock flash
|
||||||
self.logger.debug("Locking Flash")
|
self.logger.debug("Locking Flash")
|
||||||
self.FLASH_CR.LOCK = 1
|
self.FLASH_CR.LOCK = 1
|
||||||
self.FLASH_CR.store(oocd)
|
self.FLASH_CR.store()
|
||||||
|
|
||||||
# Check if flash is locked
|
# Check if flash is locked
|
||||||
self.FLASH_CR.load(oocd)
|
self.FLASH_CR.load()
|
||||||
if self.FLASH_CR.LOCK == 1:
|
if self.FLASH_CR.LOCK == 1:
|
||||||
self.logger.debug("Flash locked")
|
self.logger.debug("Flash locked")
|
||||||
else:
|
else:
|
||||||
self.logger.error("Flash lock failed")
|
self.logger.error("Flash lock failed")
|
||||||
raise Exception("Flash lock failed")
|
raise Exception("Flash lock failed")
|
||||||
|
|
||||||
def option_bytes_apply(self, oocd: OpenOCD):
|
def option_bytes_apply(self):
|
||||||
self.logger.debug("Applying Option Bytes")
|
self.logger.debug("Applying Option Bytes")
|
||||||
|
|
||||||
self.FLASH_CR.load(oocd)
|
self.FLASH_CR.load()
|
||||||
self.FLASH_CR.OPT_STRT = 1
|
self.FLASH_CR.OPT_STRT = 1
|
||||||
self.FLASH_CR.store(oocd)
|
self.FLASH_CR.store()
|
||||||
|
|
||||||
# Wait for Option Bytes to be applied
|
# Wait for Option Bytes to be applied
|
||||||
self.flash_wait_for_operation(oocd)
|
self.flash_wait_for_operation()
|
||||||
|
|
||||||
def option_bytes_load(self, oocd: OpenOCD):
|
def option_bytes_load(self):
|
||||||
self.logger.debug("Loading Option Bytes")
|
self.logger.debug("Loading Option Bytes")
|
||||||
self.FLASH_CR.load(oocd)
|
self.FLASH_CR.load()
|
||||||
self.FLASH_CR.OBL_LAUNCH = 1
|
self.FLASH_CR.OBL_LAUNCH = 1
|
||||||
self.FLASH_CR.store(oocd)
|
self.FLASH_CR.store()
|
||||||
|
|
||||||
def option_bytes_id_to_address(self, id: int) -> int:
|
def option_bytes_id_to_address(self, id: int) -> int:
|
||||||
# Check if this option byte (dword) is mapped to a register
|
# Check if this option byte (dword) is mapped to a register
|
||||||
@ -241,16 +245,16 @@ class STM32WB55:
|
|||||||
|
|
||||||
return device_reg_addr
|
return device_reg_addr
|
||||||
|
|
||||||
def flash_wait_for_operation(self, oocd: OpenOCD):
|
def flash_wait_for_operation(self):
|
||||||
# Wait for flash operation to complete
|
# Wait for flash operation to complete
|
||||||
# TODO: timeout
|
# TODO: timeout
|
||||||
while True:
|
while True:
|
||||||
self.FLASH_SR.load(oocd)
|
self.FLASH_SR.load()
|
||||||
if self.FLASH_SR.BSY == 0:
|
if self.FLASH_SR.BSY == 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
def flash_dump_status_register(self, oocd: OpenOCD):
|
def flash_dump_status_register(self):
|
||||||
self.FLASH_SR.load(oocd)
|
self.FLASH_SR.load()
|
||||||
self.logger.info(f"FLASH_SR: {self.FLASH_SR.get():08x}")
|
self.logger.info(f"FLASH_SR: {self.FLASH_SR.get():08x}")
|
||||||
if self.FLASH_SR.EOP:
|
if self.FLASH_SR.EOP:
|
||||||
self.logger.info(" End of operation")
|
self.logger.info(" End of operation")
|
||||||
@ -283,70 +287,87 @@ class STM32WB55:
|
|||||||
if self.FLASH_SR.PESD:
|
if self.FLASH_SR.PESD:
|
||||||
self.logger.info(" Programming / erase operation suspended.")
|
self.logger.info(" Programming / erase operation suspended.")
|
||||||
|
|
||||||
def write_flash_64(self, oocd: OpenOCD, address: int, word_1: int, word_2: int):
|
def write_flash_64(self, address: int, word_1: int, word_2: int):
|
||||||
self.logger.debug(f"Writing flash at address {address:08x}")
|
self.logger.debug(f"Writing flash at address {address:08x}")
|
||||||
|
|
||||||
if address % 8 != 0:
|
if address % 8 != 0:
|
||||||
self.logger.error("Address must be aligned to 8 bytes")
|
self.logger.error("Address must be aligned to 8 bytes")
|
||||||
raise Exception("Address must be aligned to 8 bytes")
|
raise Exception("Address must be aligned to 8 bytes")
|
||||||
|
|
||||||
if word_1 == oocd.read_32(address) and word_2 == oocd.read_32(address + 4):
|
if word_1 == self.openocd.read_32(address) and word_2 == self.openocd.read_32(
|
||||||
|
address + 4
|
||||||
|
):
|
||||||
self.logger.debug("Data is already programmed")
|
self.logger.debug("Data is already programmed")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.flash_unlock(oocd)
|
self.flash_unlock()
|
||||||
|
|
||||||
# Check that no flash main memory operation is ongoing by checking the BSY bit
|
# Check that no flash main memory operation is ongoing by checking the BSY bit
|
||||||
self.FLASH_SR.load(oocd)
|
self.FLASH_SR.load()
|
||||||
if self.FLASH_SR.BSY:
|
if self.FLASH_SR.BSY:
|
||||||
self.logger.error("Flash is busy")
|
self.logger.error("Flash is busy")
|
||||||
self.flash_dump_status_register(oocd)
|
self.flash_dump_status_register()
|
||||||
raise Exception("Flash is busy")
|
raise Exception("Flash is busy")
|
||||||
|
|
||||||
# Enable end of operation interrupts and error interrupts
|
# Enable end of operation interrupts and error interrupts
|
||||||
self.FLASH_CR.load(oocd)
|
self.FLASH_CR.load()
|
||||||
self.FLASH_CR.EOPIE = 1
|
self.FLASH_CR.EOPIE = 1
|
||||||
self.FLASH_CR.ERRIE = 1
|
self.FLASH_CR.ERRIE = 1
|
||||||
self.FLASH_CR.store(oocd)
|
self.FLASH_CR.store()
|
||||||
|
|
||||||
# Check that flash memory program and erase operations are allowed
|
# Check that flash memory program and erase operations are allowed
|
||||||
if self.FLASH_SR.PESD:
|
if self.FLASH_SR.PESD:
|
||||||
self.logger.error("Flash operations are not allowed")
|
self.logger.error("Flash operations are not allowed")
|
||||||
self.flash_dump_status_register(oocd)
|
self.flash_dump_status_register()
|
||||||
raise Exception("Flash operations are not allowed")
|
raise Exception("Flash operations are not allowed")
|
||||||
|
|
||||||
# Check and clear all error programming flags due to a previous programming.
|
# Check and clear all error programming flags due to a previous programming.
|
||||||
self.clear_flash_errors(oocd)
|
self.clear_flash_errors()
|
||||||
|
|
||||||
# Set the PG bit in the Flash memory control register (FLASH_CR)
|
# Set the PG bit in the Flash memory control register (FLASH_CR)
|
||||||
self.FLASH_CR.load(oocd)
|
self.FLASH_CR.load()
|
||||||
self.FLASH_CR.PG = 1
|
self.FLASH_CR.PG = 1
|
||||||
self.FLASH_CR.store(oocd)
|
self.FLASH_CR.store()
|
||||||
|
|
||||||
# Perform the data write operation at the desired memory address, only double word (64 bits) can be programmed.
|
# Perform the data write operation at the desired memory address, only double word (64 bits) can be programmed.
|
||||||
# Write the first word
|
# Write the first word
|
||||||
oocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}")
|
self.openocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}")
|
||||||
# Write the second word
|
# Write the second word
|
||||||
oocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}")
|
self.openocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}")
|
||||||
|
|
||||||
# Wait for the BSY bit to be cleared
|
# Wait for the BSY bit to be cleared
|
||||||
self.flash_wait_for_operation(oocd)
|
self.flash_wait_for_operation()
|
||||||
|
|
||||||
# Check that EOP flag is set in the FLASH_SR register
|
# Check that EOP flag is set in the FLASH_SR register
|
||||||
self.FLASH_SR.load(oocd)
|
self.FLASH_SR.load()
|
||||||
if not self.FLASH_SR.EOP:
|
if not self.FLASH_SR.EOP:
|
||||||
self.logger.error("Flash operation failed")
|
self.logger.error("Flash operation failed")
|
||||||
self.flash_dump_status_register(oocd)
|
self.flash_dump_status_register()
|
||||||
raise Exception("Flash operation failed")
|
raise Exception("Flash operation failed")
|
||||||
|
|
||||||
# Clear the EOP flag
|
# Clear the EOP flag
|
||||||
self.FLASH_SR.load(oocd)
|
self.FLASH_SR.load()
|
||||||
self.FLASH_SR.EOP = 1
|
self.FLASH_SR.EOP = 1
|
||||||
self.FLASH_SR.store(oocd)
|
self.FLASH_SR.store()
|
||||||
|
|
||||||
# Clear the PG bit in the FLASH_CR register
|
# Clear the PG bit in the FLASH_CR register
|
||||||
self.FLASH_CR.load(oocd)
|
self.FLASH_CR.load()
|
||||||
self.FLASH_CR.PG = 0
|
self.FLASH_CR.PG = 0
|
||||||
self.FLASH_CR.store(oocd)
|
self.FLASH_CR.store()
|
||||||
|
|
||||||
self.flash_lock(oocd)
|
self.flash_lock()
|
||||||
|
|
||||||
|
def option_bytes_recover(self):
|
||||||
|
self.openocd.send_tcl("mww 0x58004010 0x8000") # set OPTVERR to reset
|
||||||
|
# Replace flash_unlock and option_bytes_unlock with the following lines, if this does not work
|
||||||
|
# self.openocd.send_tcl("mww 0x58004008 0x45670123") # unlock FLASH
|
||||||
|
# self.openocd.send_tcl("mww 0x58004008 0xCDEF89AB")
|
||||||
|
# self.openocd.send_tcl("mww 0x5800400c 0x08192A3B") # unlock OB
|
||||||
|
# self.openocd.send_tcl("mww 0x5800400c 0x4C5D6E7F")
|
||||||
|
self.flash_unlock()
|
||||||
|
self.option_bytes_unlock()
|
||||||
|
self.openocd.send_tcl("mmw 0x58004020 0x3ffff1aa 0xffffffff") # Reset OB
|
||||||
|
self.openocd.send_tcl("mww 0x5800402c 0xff") # Reset WRP1AR
|
||||||
|
self.openocd.send_tcl("mww 0x58004030 0xff") # Reset WRP1BR
|
||||||
|
self.openocd.send_tcl("mmw 0x58004014 0x00020000 0") # OPTSTRT
|
||||||
|
self.openocd.send_tcl("mmw 0x58004014 0x08000000 0") # OBL_LAUNCH
|
||||||
|
|||||||
@ -22,6 +22,12 @@ class Main(App):
|
|||||||
self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes")
|
self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes")
|
||||||
self._add_args(self.parser_set)
|
self._add_args(self.parser_set)
|
||||||
self.parser_set.set_defaults(func=self.set)
|
self.parser_set.set_defaults(func=self.set)
|
||||||
|
# Set command
|
||||||
|
self.parser_recover = self.subparsers.add_parser(
|
||||||
|
"recover", help="Recover Option Bytes"
|
||||||
|
)
|
||||||
|
self._add_args(self.parser_recover)
|
||||||
|
self.parser_recover.set_defaults(func=self.recover)
|
||||||
|
|
||||||
def _add_args(self, parser):
|
def _add_args(self, parser):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
@ -75,6 +81,20 @@ class Main(App):
|
|||||||
|
|
||||||
return return_code
|
return return_code
|
||||||
|
|
||||||
|
def recover(self):
|
||||||
|
self.logger.info("Setting Option Bytes")
|
||||||
|
|
||||||
|
# OpenOCD
|
||||||
|
openocd = OpenOCDProgrammer(
|
||||||
|
self.args.interface,
|
||||||
|
self.args.port_base,
|
||||||
|
self.args.serial,
|
||||||
|
)
|
||||||
|
|
||||||
|
openocd.option_bytes_recover()
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
Main()()
|
Main()()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user