diff --git a/builder/frameworks/beken-72xx-sdk.py b/builder/frameworks/beken-72xx-sdk.py index 8a7cece..f48d505 100644 --- a/builder/frameworks/beken-72xx-sdk.py +++ b/builder/frameworks/beken-72xx-sdk.py @@ -552,6 +552,12 @@ env.Replace( SIZEPRINTCMD="$SIZETOOL -B -d $SOURCES", ) +# Calculate RBL header offset +app_offs = int(env["FLASH_APP_OFFSET"], 16) +app_size = int(board.get("build.bkrbl_size_app"), 16) +rbl_offs = int(app_size // 32 * 34) - 102 +env.Replace(FLASH_RBL_OFFSET=f"0x{app_offs + rbl_offs:06X}") + # Build all libraries env.BuildLibraries() @@ -559,4 +565,21 @@ env.BuildLibraries() env.Replace( # linker command (encryption + packaging) LINK="${LINK2BIN} ${VARIANT} '' ''", + # UF2OTA input list + UF2OTA=[ + # app binary image (enc+crc), OTA1 (uploader) only + ( + "app", + "${BUILD_DIR}/${MCULC}_app_${FLASH_APP_OFFSET}.crc", + "", + "", + ), + # app RBL header (crc), OTA1 (uploader) only + ( + f"app+{rbl_offs}", + "${BUILD_DIR}/${MCULC}_app_${FLASH_RBL_OFFSET}.rblh", + "", # not used for OTA2 + "", + ), + ], ) diff --git a/builder/utils/env.py b/builder/utils/env.py index b5c06fa..2955525 100644 --- a/builder/utils/env.py +++ b/builder/utils/env.py @@ -32,6 +32,7 @@ def env_add_defaults(env, platform, board): LDSCRIPT_PATH=["${LDSCRIPT_SDK}"], # Board config variables MCU=board.get("build.mcu").upper(), + MCULC=board.get("build.mcu"), VARIANT=board.get("build.variant"), LDSCRIPT_SDK=board.get("build.ldscript_sdk"), LDSCRIPT_ARDUINO=board.get("build.ldscript_arduino"), diff --git a/tools/soc/link2bin_bk72xx.py b/tools/soc/link2bin_bk72xx.py index 23b6982..8d9a6ce 100644 --- a/tools/soc/link2bin_bk72xx.py +++ b/tools/soc/link2bin_bk72xx.py @@ -5,7 +5,7 @@ from os import stat from os.path import basename from typing import Tuple -from tools.util.bkutil import RBL, BekenBinary +from tools.util.bkutil import RBL, BekenBinary, DataType from tools.util.fileio import chext, chname, isnewer, writebin, writejson from tools.util.models import Family from tools.util.obj import get @@ -31,9 +31,10 @@ def elf2bin_bk72xx( app_addr = nmap["_vector_start"] app_offs = calc_offset(app_addr) app_size = int(rbl_size, 16) + rbl_offs = app_offs # build output name - output = chname(input, f"{mcu}_app_0x{app_offs:06X}.bin") + output = chname(input, f"{mcu}_app_0x{app_offs:06X}.rbl") fw_bin = chext(input, "bin") # print graph element print(f"| |-- {basename(output)}") @@ -53,5 +54,42 @@ def elf2bin_bk72xx( fw_size = stat(fw_bin).st_size raw = open(fw_bin, "rb") out = open(output, "wb") - for data in bk.package(raw, app_addr, fw_size, rbl): + + # open encrypted+CRC binary output + out_crc = chname(input, f"{mcu}_app_0x{app_offs:06X}.crc") + print(f"| |-- {basename(out_crc)}") + crc = open(out_crc, "wb") + + # get partial (type, bytes) data generator + package_gen = bk.package(raw, app_addr, fw_size, rbl, partial=True) + + # write all BINARY blocks + for data_type, data in package_gen: + if data_type != DataType.BINARY: + break out.write(data) + crc.write(data) + rbl_offs += len(data) + + # skip PADDING_SIZE bytes for RBL header, write it to main output + if data_type == DataType.PADDING_SIZE: + out.write(b"\xff" * data) + rbl_offs += data + + # open RBL header output + out_rblh = chname(input, f"{mcu}_app_0x{rbl_offs:06X}.rblh") + print(f"| |-- {basename(out_rblh)}") + rblh = open(out_rblh, "wb") + + # write all RBL blocks + for data_type, data in package_gen: + if data_type != DataType.RBL: + break + out.write(data) + rblh.write(data) + + # close all files + raw.close() + out.close() + crc.close() + rblh.close() diff --git a/tools/uf2ota/models.py b/tools/uf2ota/models.py index 2678a70..a90748e 100644 --- a/tools/uf2ota/models.py +++ b/tools/uf2ota/models.py @@ -91,19 +91,19 @@ class Input: if input[0] and input[1]: if "+" in input[0]: (self.ota1_part, self.ota1_offs) = input[0].split("+") - self.ota1_offs = int(self.ota1_offs, 16) + self.ota1_offs = int(self.ota1_offs, 0) else: self.ota1_part = input[0] self.ota1_file = input[1] if input[2] and input[3]: if "+" in input[2]: (self.ota2_part, self.ota2_offs) = input[2].split("+") - self.ota2_offs = int(self.ota2_offs, 16) + self.ota2_offs = int(self.ota2_offs, 0) else: self.ota2_part = input[2] self.ota2_file = input[3] - if self.ota1_offs != self.ota2_offs: + if self.ota1_file and self.ota2_file and self.ota1_offs != self.ota2_offs: # currently, offsets cannot differ when storing images # (this would require to actually store it twice) raise ValueError(f"Offsets cannot differ ({self.ota1_file})") diff --git a/tools/util/bkutil.py b/tools/util/bkutil.py index 5d37fd0..8792187 100644 --- a/tools/util/bkutil.py +++ b/tools/util/bkutil.py @@ -8,12 +8,12 @@ sys.path.append(join(dirname(__file__), "..", "..")) from argparse import ArgumentParser, FileType from binascii import crc32 from dataclasses import dataclass, field -from enum import IntFlag +from enum import Enum, IntFlag from io import SEEK_SET, FileIO from os import stat from struct import Struct from time import time -from typing import Union +from typing import Generator, Tuple, Union from tools.util.bitint import BitInt from tools.util.bkcrypto import BekenCrypto @@ -34,6 +34,17 @@ from tools.util.intbin import ( ) +class DataType(Enum): + BINARY = "BINARY" + PADDING_SIZE = "PADDING_SIZE" + RBL = "RBL" + + +DataTuple = Tuple[DataType, Union[bytes, int]] +DataUnion = Union[bytes, DataTuple] +DataGenerator = Generator[DataUnion, None, None] + + class OTAAlgorithm(IntFlag): NONE = 0 CRYPT_XOR = 1 @@ -125,11 +136,14 @@ class BekenBinary: coeffs = list(map(BitInt, map(betoint, biniter(coeffs, 4)))) self.crypto = BekenCrypto(coeffs) - def crc(self, data: ByteGenerator) -> ByteGenerator: + def crc(self, data: ByteGenerator, type: DataType = None) -> DataGenerator: for block in geniter(data, 32): crc = CRC16.CMS.calc(block) - yield block - yield inttobe16(crc) + block += inttobe16(crc) + if type: + yield (type, block) + else: + yield block def uncrc(self, data: ByteGenerator, check: bool = True) -> ByteGenerator: for block in geniter(data, 34): @@ -149,11 +163,23 @@ class BekenBinary: yield word addr += 4 - def package(self, f: FileIO, addr: int, size: int, rbl: RBL) -> ByteGenerator: + def package( + self, + f: FileIO, + addr: int, + size: int, + rbl: RBL, + partial: bool = False, + ) -> DataGenerator: if not rbl.container_size: raise ValueError("RBL must have a total size when packaging") crc_total = 0 + # yield all data as (type, bytes) tuples, if partial mode enabled + type_binary = DataType.BINARY if partial else None + type_padding = DataType.PADDING_SIZE if partial else None + type_rbl = DataType.RBL if partial else None + # when to stop reading input data data_end = size if rbl.has_part_table: @@ -169,7 +195,7 @@ class BekenBinary: # iterate over encrypted 32-byte blocks for block in geniter(data_crypt_gen, 32): # add CRC16 and yield - yield from self.crc(block) + yield from self.crc(block, type_binary) crc_total += 2 rbl.update(block) @@ -188,16 +214,19 @@ class BekenBinary: # add last padding with normal values buf += b"\xff" * 16 # yield the temporary buffer - yield from self.crc(buf) + yield from self.crc(buf, type_binary) crc_total += 2 * (len(buf) // 32) # pad the entire container with 0xFF, excluding RBL and its CRC16 pad_size = pad_up(rbl.data_size + crc_total, rbl.container_size_crc) - 102 - for _ in range(pad_size): - yield b"\xff" + if type_padding: + yield (type_padding, pad_size) + else: + for _ in range(pad_size): + yield b"\xff" # yield RBL with CRC16 - yield from self.crc(rbl.serialize()) + yield from self.crc(rbl.serialize(), type_rbl) def auto_int(x):