image loader improvements
All checks were successful
Rust/va416xx-rs/pipeline/pr-main This commit looks good
All checks were successful
Rust/va416xx-rs/pipeline/pr-main This commit looks good
This commit is contained in:
parent
9c01c10d74
commit
edd5d73b5b
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from typing import List
|
from typing import List, Tuple
|
||||||
from spacepackets.ecss.defs import PusService
|
from spacepackets.ecss.defs import PusService
|
||||||
from spacepackets.ecss.tm import PusTm
|
from spacepackets.ecss.tm import PusTm
|
||||||
from tmtccmd.com import ComInterface
|
from tmtccmd.com import ComInterface
|
||||||
@ -60,6 +60,7 @@ class ActionId(enum.IntEnum):
|
|||||||
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
SEQ_PROVIDER = SeqCountProvider(bit_width=14)
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
@ -70,7 +71,174 @@ class LoadableSegment:
|
|||||||
data: bytes
|
data: bytes
|
||||||
|
|
||||||
|
|
||||||
SEQ_PROVIDER = SeqCountProvider(bit_width=14)
|
class Target(enum.Enum):
|
||||||
|
BOOTLOADER = 0
|
||||||
|
APP_A = 1
|
||||||
|
APP_B = 1
|
||||||
|
|
||||||
|
|
||||||
|
class ImageLoader:
|
||||||
|
def __init__(self, com_if: ComInterface, verificator: PusVerificator) -> None:
|
||||||
|
self.com_if = com_if
|
||||||
|
self.verificator = verificator
|
||||||
|
|
||||||
|
def handle_ping_cmd(self):
|
||||||
|
_LOGGER.info("Sending ping command")
|
||||||
|
ping_tc = PusTc(
|
||||||
|
apid=0x00,
|
||||||
|
service=PusService.S17_TEST,
|
||||||
|
subservice=1,
|
||||||
|
seq_count=SEQ_PROVIDER.get_and_increment(),
|
||||||
|
app_data=bytes(PING_PAYLOAD_SIZE),
|
||||||
|
)
|
||||||
|
self.verificator.add_tc(ping_tc)
|
||||||
|
self.com_if.send(bytes(ping_tc.pack()))
|
||||||
|
|
||||||
|
data_available = self.com_if.data_available(0.4)
|
||||||
|
if not data_available:
|
||||||
|
_LOGGER.warning("no ping reply received")
|
||||||
|
for reply in self.com_if.receive():
|
||||||
|
result = self.verificator.add_tm(
|
||||||
|
Service1Tm.from_tm(PusTm.unpack(reply, 0), UnpackParams(0))
|
||||||
|
)
|
||||||
|
if result is not None and result.completed:
|
||||||
|
_LOGGER.info("received ping completion reply")
|
||||||
|
|
||||||
|
def handle_corruption_cmd(self, target: Target):
|
||||||
|
|
||||||
|
if target == Target.BOOTLOADER:
|
||||||
|
_LOGGER.error("can not corrupt bootloader")
|
||||||
|
if target == Target.APP_A:
|
||||||
|
self.send_tc(
|
||||||
|
PusTc(
|
||||||
|
apid=0,
|
||||||
|
service=ACTION_SERVICE,
|
||||||
|
subservice=ActionId.CORRUPT_APP_A,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if target == Target.APP_B:
|
||||||
|
self.send_tc(
|
||||||
|
PusTc(
|
||||||
|
apid=0,
|
||||||
|
service=ACTION_SERVICE,
|
||||||
|
subservice=ActionId.CORRUPT_APP_B,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle_flash_cmd(self, target: Target, file_path: Path) -> int:
|
||||||
|
loadable_segments = []
|
||||||
|
_LOGGER.info("Parsing ELF file for loadable sections")
|
||||||
|
total_size = 0
|
||||||
|
loadable_segments, total_size = create_loadable_segments(target, file_path)
|
||||||
|
segments_info_str(target, loadable_segments, total_size, file_path)
|
||||||
|
result = self._perform_flashing_algorithm(loadable_segments)
|
||||||
|
if result != 0:
|
||||||
|
return result
|
||||||
|
self._crc_and_app_size_postprocessing(target, total_size, loadable_segments)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _perform_flashing_algorithm(
|
||||||
|
self,
|
||||||
|
loadable_segments: List[LoadableSegment],
|
||||||
|
) -> int:
|
||||||
|
# Perform the flashing algorithm.
|
||||||
|
for segment in loadable_segments:
|
||||||
|
segment_end = segment.offset + segment.size
|
||||||
|
current_addr = segment.offset
|
||||||
|
pos_in_segment = 0
|
||||||
|
while pos_in_segment < segment.size:
|
||||||
|
next_chunk_size = min(segment_end - current_addr, CHUNK_SIZE)
|
||||||
|
data = segment.data[pos_in_segment : pos_in_segment + next_chunk_size]
|
||||||
|
next_packet = pack_memory_write_command(current_addr, data)
|
||||||
|
_LOGGER.info(
|
||||||
|
f"Sending memory write command for address {current_addr:#08x} and data with "
|
||||||
|
f"length {len(data)}"
|
||||||
|
)
|
||||||
|
self.verificator.add_tc(next_packet)
|
||||||
|
self.com_if.send(bytes(next_packet.pack()))
|
||||||
|
current_addr += next_chunk_size
|
||||||
|
pos_in_segment += next_chunk_size
|
||||||
|
start_time = time.time()
|
||||||
|
while True:
|
||||||
|
if time.time() - start_time > 1.0:
|
||||||
|
_LOGGER.error("Timeout while waiting for reply")
|
||||||
|
return -1
|
||||||
|
data_available = self.com_if.data_available(0.1)
|
||||||
|
done = False
|
||||||
|
if not data_available:
|
||||||
|
continue
|
||||||
|
replies = self.com_if.receive()
|
||||||
|
for reply in replies:
|
||||||
|
tm = PusTm.unpack(reply, 0)
|
||||||
|
if tm.service != 1:
|
||||||
|
continue
|
||||||
|
service_1_tm = Service1Tm.from_tm(tm, UnpackParams(0))
|
||||||
|
check_result = self.verificator.add_tm(service_1_tm)
|
||||||
|
# We could send after we have received the step reply, but that can
|
||||||
|
# somehow lead to overrun errors. I think it's okay to do it like
|
||||||
|
# this as long as the flash loader only uses polling..
|
||||||
|
if (
|
||||||
|
check_result is not None
|
||||||
|
and check_result.status.completed == StatusField.SUCCESS
|
||||||
|
):
|
||||||
|
done = True
|
||||||
|
|
||||||
|
# This is an optimized variant, but I think the small delay is not an issue.
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
check_result is not None
|
||||||
|
and check_result.status.step == StatusField.SUCCESS
|
||||||
|
and len(check_result.status.step_list) == 1
|
||||||
|
):
|
||||||
|
done = True
|
||||||
|
"""
|
||||||
|
self.verificator.remove_completed_entries()
|
||||||
|
if done:
|
||||||
|
break
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _crc_and_app_size_postprocessing(
|
||||||
|
self,
|
||||||
|
target: Target,
|
||||||
|
total_size: int,
|
||||||
|
loadable_segments: List[LoadableSegment],
|
||||||
|
):
|
||||||
|
if target == Target.BOOTLOADER:
|
||||||
|
_LOGGER.info("Blanking the bootloader checksum")
|
||||||
|
# Blank the checksum. For the bootloader, the bootloader will calculate the
|
||||||
|
# checksum itself on the initial run.
|
||||||
|
checksum_write_packet = pack_memory_write_command(
|
||||||
|
BOOTLOADER_CRC_ADDR, bytes([0x00, 0x00, 0x00, 0x00])
|
||||||
|
)
|
||||||
|
self.send_tc(checksum_write_packet)
|
||||||
|
else:
|
||||||
|
crc_addr = None
|
||||||
|
size_addr = None
|
||||||
|
if target == Target.APP_A:
|
||||||
|
crc_addr = APP_A_CRC_ADDR
|
||||||
|
size_addr = APP_A_SIZE_ADDR
|
||||||
|
elif target == Target.APP_B:
|
||||||
|
crc_addr = APP_B_CRC_ADDR
|
||||||
|
size_addr = APP_B_SIZE_ADDR
|
||||||
|
assert crc_addr is not None
|
||||||
|
assert size_addr is not None
|
||||||
|
_LOGGER.info(f"Writing app size {total_size} at address {size_addr:#08x}")
|
||||||
|
size_write_packet = pack_memory_write_command(
|
||||||
|
size_addr, struct.pack("!I", total_size)
|
||||||
|
)
|
||||||
|
self.com_if.send(bytes(size_write_packet.pack()))
|
||||||
|
time.sleep(0.2)
|
||||||
|
crc_calc = PredefinedCrc("crc-32")
|
||||||
|
for segment in loadable_segments:
|
||||||
|
crc_calc.update(segment.data)
|
||||||
|
checksum = crc_calc.digest()
|
||||||
|
_LOGGER.info(
|
||||||
|
f"Writing checksum 0x[{checksum.hex(sep=',')}] at address {crc_addr:#08x}"
|
||||||
|
)
|
||||||
|
self.send_tc(pack_memory_write_command(crc_addr, checksum))
|
||||||
|
|
||||||
|
def send_tc(self, tc: PusTc):
|
||||||
|
self.com_if.send(bytes(tc.pack()))
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
@ -110,262 +278,133 @@ def main() -> int:
|
|||||||
verificator = PusVerificator()
|
verificator = PusVerificator()
|
||||||
com_if = SerialCobsComIF(serial_cfg)
|
com_if = SerialCobsComIF(serial_cfg)
|
||||||
com_if.open()
|
com_if.open()
|
||||||
|
target = None
|
||||||
|
if args.target == "bl":
|
||||||
|
target = Target.BOOTLOADER
|
||||||
|
elif args.target == "a":
|
||||||
|
target = Target.APP_A
|
||||||
|
elif args.target == "b":
|
||||||
|
target = Target.APP_B
|
||||||
|
image_loader = ImageLoader(com_if, verificator)
|
||||||
file_path = None
|
file_path = None
|
||||||
|
result = -1
|
||||||
if args.ping:
|
if args.ping:
|
||||||
_LOGGER.info("Sending ping command")
|
image_loader.handle_ping_cmd()
|
||||||
ping_tc = PusTc(
|
result = 0
|
||||||
apid=0x00,
|
if target:
|
||||||
service=PusService.S17_TEST,
|
|
||||||
subservice=1,
|
|
||||||
seq_count=SEQ_PROVIDER.get_and_increment(),
|
|
||||||
app_data=bytes(PING_PAYLOAD_SIZE),
|
|
||||||
)
|
|
||||||
verificator.add_tc(ping_tc)
|
|
||||||
com_if.send(ping_tc.pack())
|
|
||||||
|
|
||||||
data_available = com_if.data_available(0.4)
|
|
||||||
if not data_available:
|
|
||||||
_LOGGER.warning("no ping reply received")
|
|
||||||
for reply in com_if.receive():
|
|
||||||
result = verificator.add_tm(
|
|
||||||
Service1Tm.from_tm(PusTm.unpack(reply, 0), UnpackParams(0))
|
|
||||||
)
|
|
||||||
if result is not None and result.completed:
|
|
||||||
_LOGGER.info("received ping completion reply")
|
|
||||||
if not args.target:
|
|
||||||
return 0
|
|
||||||
if args.target:
|
|
||||||
if not args.corrupt:
|
if not args.corrupt:
|
||||||
if not args.path:
|
if not args.path:
|
||||||
_LOGGER.error("App Path needs to be specified for the flash process")
|
_LOGGER.error("App Path needs to be specified for the flash process")
|
||||||
return -1
|
|
||||||
file_path = Path(args.path)
|
file_path = Path(args.path)
|
||||||
if not file_path.exists():
|
if not file_path.exists():
|
||||||
_LOGGER.error("File does not exist")
|
_LOGGER.error("File does not exist")
|
||||||
return -1
|
|
||||||
if args.corrupt:
|
if args.corrupt:
|
||||||
if not args.target:
|
if target:
|
||||||
|
image_loader.handle_corruption_cmd(target)
|
||||||
|
result = 0
|
||||||
|
else:
|
||||||
_LOGGER.error("target for corruption command required")
|
_LOGGER.error("target for corruption command required")
|
||||||
return -1
|
|
||||||
if args.target == "bl":
|
|
||||||
_LOGGER.error("can not corrupt bootloader")
|
|
||||||
if args.target == "a":
|
|
||||||
packet = PusTc(
|
|
||||||
apid=0,
|
|
||||||
service=ACTION_SERVICE,
|
|
||||||
subservice=ActionId.CORRUPT_APP_A,
|
|
||||||
)
|
|
||||||
com_if.send(packet.pack())
|
|
||||||
if args.target == "b":
|
|
||||||
packet = PusTc(
|
|
||||||
apid=0,
|
|
||||||
service=ACTION_SERVICE,
|
|
||||||
subservice=ActionId.CORRUPT_APP_B,
|
|
||||||
)
|
|
||||||
com_if.send(packet.pack())
|
|
||||||
else:
|
else:
|
||||||
assert file_path is not None
|
assert file_path is not None
|
||||||
loadable_segments = []
|
assert target is not None
|
||||||
_LOGGER.info("Parsing ELF file for loadable sections")
|
result = image_loader.handle_flash_cmd(target, file_path)
|
||||||
total_size = 0
|
|
||||||
with open(file_path, "rb") as app_file:
|
|
||||||
elf_file = ELFFile(app_file)
|
|
||||||
|
|
||||||
for (idx, segment) in enumerate(elf_file.iter_segments("PT_LOAD")):
|
|
||||||
if segment.header.p_filesz == 0:
|
|
||||||
continue
|
|
||||||
# Basic validity checks of the base addresses.
|
|
||||||
if idx == 0:
|
|
||||||
if (
|
|
||||||
args.target == "bl"
|
|
||||||
and segment.header.p_paddr != BOOTLOADER_START_ADDR
|
|
||||||
):
|
|
||||||
raise ValueError(
|
|
||||||
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
|
|
||||||
f"bootloader, expected {BOOTLOADER_START_ADDR}"
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
args.target == "a"
|
|
||||||
and segment.header.p_paddr != APP_A_START_ADDR
|
|
||||||
):
|
|
||||||
raise ValueError(
|
|
||||||
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
|
|
||||||
f"App A, expected {APP_A_START_ADDR}"
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
args.target == "b"
|
|
||||||
and segment.header.p_paddr != APP_B_START_ADDR
|
|
||||||
):
|
|
||||||
raise ValueError(
|
|
||||||
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
|
|
||||||
f"App B, expected {APP_B_START_ADDR}"
|
|
||||||
)
|
|
||||||
name = None
|
|
||||||
for section in elf_file.iter_sections():
|
|
||||||
if (
|
|
||||||
section.header.sh_offset == segment.header.p_offset
|
|
||||||
and section.header.sh_size > 0
|
|
||||||
):
|
|
||||||
name = section.name
|
|
||||||
if name is None:
|
|
||||||
_LOGGER.warning("no fitting section found for segment")
|
|
||||||
continue
|
|
||||||
# print(f"Segment Addr: {segment.header.p_paddr}")
|
|
||||||
# print(f"Segment Offset: {segment.header.p_offset}")
|
|
||||||
# print(f"Segment Filesize: {segment.header.p_filesz}")
|
|
||||||
loadable_segments.append(
|
|
||||||
LoadableSegment(
|
|
||||||
name=name,
|
|
||||||
offset=segment.header.p_paddr,
|
|
||||||
size=segment.header.p_filesz,
|
|
||||||
data=segment.data(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
total_size += segment.header.p_filesz
|
|
||||||
context_str = None
|
|
||||||
|
|
||||||
# Set context string and perform basic sanity checks.
|
|
||||||
if args.target == "bl":
|
|
||||||
if total_size > BOOTLOADER_MAX_SIZE:
|
|
||||||
_LOGGER.error(
|
|
||||||
f"provided bootloader app larger than allowed {total_size} bytes"
|
|
||||||
)
|
|
||||||
return -1
|
|
||||||
context_str = "Bootloader"
|
|
||||||
elif args.target == "a":
|
|
||||||
if total_size > APP_A_MAX_SIZE:
|
|
||||||
_LOGGER.error(
|
|
||||||
f"provided App A larger than allowed {total_size} bytes"
|
|
||||||
)
|
|
||||||
return -1
|
|
||||||
context_str = "App Slot A"
|
|
||||||
elif args.target == "b":
|
|
||||||
if total_size > APP_B_MAX_SIZE:
|
|
||||||
_LOGGER.error(
|
|
||||||
f"provided App B larger than allowed {total_size} bytes"
|
|
||||||
)
|
|
||||||
return -1
|
|
||||||
context_str = "App Slot B"
|
|
||||||
_LOGGER.info(
|
|
||||||
f"Flashing {context_str} with image {file_path} (size {total_size})"
|
|
||||||
)
|
|
||||||
for idx, segment in enumerate(loadable_segments):
|
|
||||||
_LOGGER.info(
|
|
||||||
f"Loadable section {idx} {segment.name} with offset {segment.offset:#08x} and "
|
|
||||||
f"size {segment.size}"
|
|
||||||
)
|
|
||||||
result = perform_flashing_algorithm(com_if, loadable_segments, verificator)
|
|
||||||
if result != 0:
|
|
||||||
return result
|
|
||||||
crc_and_app_size_postprocessing(
|
|
||||||
com_if, args.target, total_size, loadable_segments
|
|
||||||
)
|
|
||||||
com_if.close()
|
com_if.close()
|
||||||
return 0
|
return result
|
||||||
|
|
||||||
|
|
||||||
def perform_flashing_algorithm(
|
def create_loadable_segments(
|
||||||
com_if: ComInterface,
|
target: Target, file_path: Path
|
||||||
loadable_segments: List[LoadableSegment],
|
) -> Tuple[List[LoadableSegment], int]:
|
||||||
verificator: PusVerificator,
|
loadable_segments = []
|
||||||
) -> int:
|
total_size = 0
|
||||||
# Perform the flashing algorithm.
|
with open(file_path, "rb") as app_file:
|
||||||
for segment in loadable_segments:
|
elf_file = ELFFile(app_file)
|
||||||
segment_end = segment.offset + segment.size
|
|
||||||
current_addr = segment.offset
|
for idx, segment in enumerate(elf_file.iter_segments("PT_LOAD")):
|
||||||
pos_in_segment = 0
|
if segment.header.p_filesz == 0:
|
||||||
while pos_in_segment < segment.size:
|
continue
|
||||||
next_chunk_size = min(segment_end - current_addr, CHUNK_SIZE)
|
# Basic validity checks of the base addresses.
|
||||||
data = segment.data[pos_in_segment : pos_in_segment + next_chunk_size]
|
if idx == 0:
|
||||||
next_packet = pack_memory_write_command(current_addr, data)
|
if (
|
||||||
_LOGGER.info(
|
target == Target.BOOTLOADER
|
||||||
f"Sending memory write command for address {current_addr:#08x} and data with "
|
and segment.header.p_paddr != BOOTLOADER_START_ADDR
|
||||||
f"length {len(data)}"
|
):
|
||||||
|
raise ValueError(
|
||||||
|
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
|
||||||
|
f"bootloader, expected {BOOTLOADER_START_ADDR}"
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
target == Target.APP_A
|
||||||
|
and segment.header.p_paddr != APP_A_START_ADDR
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
|
||||||
|
f"App A, expected {APP_A_START_ADDR}"
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
target == Target.APP_B
|
||||||
|
and segment.header.p_paddr != APP_B_START_ADDR
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
|
||||||
|
f"App B, expected {APP_B_START_ADDR}"
|
||||||
|
)
|
||||||
|
name = None
|
||||||
|
for section in elf_file.iter_sections():
|
||||||
|
if (
|
||||||
|
section.header.sh_offset == segment.header.p_offset
|
||||||
|
and section.header.sh_size > 0
|
||||||
|
):
|
||||||
|
name = section.name
|
||||||
|
if name is None:
|
||||||
|
_LOGGER.warning("no fitting section found for segment")
|
||||||
|
continue
|
||||||
|
# print(f"Segment Addr: {segment.header.p_paddr}")
|
||||||
|
# print(f"Segment Offset: {segment.header.p_offset}")
|
||||||
|
# print(f"Segment Filesize: {segment.header.p_filesz}")
|
||||||
|
loadable_segments.append(
|
||||||
|
LoadableSegment(
|
||||||
|
name=name,
|
||||||
|
offset=segment.header.p_paddr,
|
||||||
|
size=segment.header.p_filesz,
|
||||||
|
data=segment.data(),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
verificator.add_tc(next_packet)
|
total_size += segment.header.p_filesz
|
||||||
com_if.send(next_packet.pack())
|
return loadable_segments, total_size
|
||||||
current_addr += next_chunk_size
|
|
||||||
pos_in_segment += next_chunk_size
|
|
||||||
start_time = time.time()
|
|
||||||
while True:
|
|
||||||
if time.time() - start_time > 1.0:
|
|
||||||
_LOGGER.error("Timeout while waiting for reply")
|
|
||||||
return -1
|
|
||||||
data_available = com_if.data_available(0.1)
|
|
||||||
done = False
|
|
||||||
if not data_available:
|
|
||||||
continue
|
|
||||||
replies = com_if.receive()
|
|
||||||
for reply in replies:
|
|
||||||
tm = PusTm.unpack(reply, 0)
|
|
||||||
if tm.service != 1:
|
|
||||||
continue
|
|
||||||
service_1_tm = Service1Tm.from_tm(tm, UnpackParams(0))
|
|
||||||
check_result = verificator.add_tm(service_1_tm)
|
|
||||||
# We could send after we have received the step reply, but that can
|
|
||||||
# somehow lead to overrun errors. I think it's okay to do it like
|
|
||||||
# this as long as the flash loader only uses polling..
|
|
||||||
if (
|
|
||||||
check_result is not None
|
|
||||||
and check_result.status.completed == StatusField.SUCCESS
|
|
||||||
):
|
|
||||||
done = True
|
|
||||||
|
|
||||||
# This is an optimized variant, but I think the small delay is not an issue.
|
|
||||||
"""
|
|
||||||
if (
|
|
||||||
check_result is not None
|
|
||||||
and check_result.status.step == StatusField.SUCCESS
|
|
||||||
and len(check_result.status.step_list) == 1
|
|
||||||
):
|
|
||||||
done = True
|
|
||||||
"""
|
|
||||||
verificator.remove_completed_entries()
|
|
||||||
if done:
|
|
||||||
break
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def crc_and_app_size_postprocessing(
|
def segments_info_str(
|
||||||
com_if: ComInterface,
|
target: Target,
|
||||||
target: str,
|
|
||||||
total_size: int,
|
|
||||||
loadable_segments: List[LoadableSegment],
|
loadable_segments: List[LoadableSegment],
|
||||||
|
total_size: int,
|
||||||
|
file_path: Path,
|
||||||
):
|
):
|
||||||
if target == "bl":
|
# Set context string and perform basic sanity checks.
|
||||||
_LOGGER.info("Blanking the bootloader checksum")
|
if target == Target.BOOTLOADER:
|
||||||
# Blank the checksum. For the bootloader, the bootloader will calculate the
|
if total_size > BOOTLOADER_MAX_SIZE:
|
||||||
# checksum itself on the initial run.
|
_LOGGER.error(
|
||||||
checksum_write_packet = pack_memory_write_command(
|
f"provided bootloader app larger than allowed {total_size} bytes"
|
||||||
BOOTLOADER_CRC_ADDR, bytes([0x00, 0x00, 0x00, 0x00])
|
)
|
||||||
)
|
return -1
|
||||||
com_if.send(checksum_write_packet.pack())
|
context_str = "Bootloader"
|
||||||
else:
|
elif target == Target.APP_A:
|
||||||
crc_addr = None
|
if total_size > APP_A_MAX_SIZE:
|
||||||
size_addr = None
|
_LOGGER.error(f"provided App A larger than allowed {total_size} bytes")
|
||||||
if target == "a":
|
return -1
|
||||||
crc_addr = APP_A_CRC_ADDR
|
context_str = "App Slot A"
|
||||||
size_addr = APP_A_SIZE_ADDR
|
elif target == Target.APP_B:
|
||||||
elif target == "b":
|
if total_size > APP_B_MAX_SIZE:
|
||||||
crc_addr = APP_B_CRC_ADDR
|
_LOGGER.error(f"provided App B larger than allowed {total_size} bytes")
|
||||||
size_addr = APP_B_SIZE_ADDR
|
return -1
|
||||||
assert crc_addr is not None
|
context_str = "App Slot B"
|
||||||
assert size_addr is not None
|
_LOGGER.info(f"Flashing {context_str} with image {file_path} (size {total_size})")
|
||||||
_LOGGER.info(f"Writing app size {total_size} at address {size_addr:#08x}")
|
for idx, segment in enumerate(loadable_segments):
|
||||||
size_write_packet = pack_memory_write_command(
|
|
||||||
size_addr, struct.pack("!I", total_size)
|
|
||||||
)
|
|
||||||
com_if.send(size_write_packet.pack())
|
|
||||||
time.sleep(0.2)
|
|
||||||
crc_calc = PredefinedCrc("crc-32")
|
|
||||||
for segment in loadable_segments:
|
|
||||||
crc_calc.update(segment.data)
|
|
||||||
checksum = crc_calc.digest()
|
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
f"Writing checksum 0x[{checksum.hex(sep=',')}] at address {crc_addr:#08x}"
|
f"Loadable section {idx} {segment.name} with offset {segment.offset:#08x} and "
|
||||||
|
f"size {segment.size}"
|
||||||
)
|
)
|
||||||
checksum_write_packet = pack_memory_write_command(crc_addr, checksum)
|
|
||||||
com_if.send(checksum_write_packet.pack())
|
|
||||||
|
|
||||||
|
|
||||||
def pack_memory_write_command(addr: int, data: bytes) -> PusTc:
|
def pack_memory_write_command(addr: int, data: bytes) -> PusTc:
|
||||||
@ -381,7 +420,7 @@ def pack_memory_write_command(addr: int, data: bytes) -> PusTc:
|
|||||||
service=MEMORY_SERVICE,
|
service=MEMORY_SERVICE,
|
||||||
subservice=RAW_MEMORY_WRITE_SUBSERVICE,
|
subservice=RAW_MEMORY_WRITE_SUBSERVICE,
|
||||||
seq_count=SEQ_PROVIDER.get_and_increment(),
|
seq_count=SEQ_PROVIDER.get_and_increment(),
|
||||||
app_data=app_data,
|
app_data=bytes(app_data),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user