Skip to content

Commit 3c5f4a2

Browse files
authored
add Tuya coeff-key OpenBK7252 build to release (#2152)
* add Tuya BK7252U back * Update .releaserc.yaml * rbl * Probe BK7252 Tuya OTA readback CRC * Fix BK7252 Tuya OTA partition table * Restore BK7252 Tuya OTA artefact * Probe BK7252 OTA flash readback CRC * patch RT bootloader * remove releaser * use standard partitions * Update Makefile * Create bk7252_partition_2M.json
1 parent 8395dca commit 3c5f4a2

5 files changed

Lines changed: 291 additions & 1 deletion

File tree

.github/workflows/workflow.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,9 +317,11 @@ jobs:
317317
name: ${{ env.APP_NAME }}_${{ needs.refs.outputs.version }}_${{ matrix.platform }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}
318318
path: |
319319
output/${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}/${{ matrix.platform }}_UA_${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}.bin
320+
output/${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}/${{ matrix.platform }}_Tuya_UA_${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}.bin
320321
output/${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}/${{ matrix.platform }}_UG_${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}.bin
321322
output/${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}/${{ matrix.platform }}_UG_${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}.img
322323
output/${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}/${{ matrix.platform }}_QIO_${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}.bin
324+
output/${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}/${{ matrix.platform }}_Tuya_QIO_${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}.bin
323325
output/${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}/OpenBK7231M_QIO_${{ needs.refs.outputs.version }}.bin
324326
output/${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}/OpenBK7231M_ALT_QIO_${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}.bin
325327
output/${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}/${{ matrix.platform }}_${{ needs.refs.outputs.version }}${{ matrix.variant != 'default' && '_' || '' }}${{ matrix.variant != 'default' && matrix.variant || '' }}.rbl

Makefile

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,23 @@ OpenBK7231U: prebuild_OpenBK7231U
726726

727727
.PHONY: OpenBK7252
728728
OpenBK7252: prebuild_OpenBK7252
729-
cd sdk/beken_freertos_sdk && ARM_GCC_TOOLCHAIN=$(PWD)/sdk/beken_freertos_sdk/toolchain/arm-none-eabi/bin/ OBK_VARIANT=$(OBK_VARIANT) sh build.sh bk7251 $(APP_VERSION)
729+
cd sdk/beken_freertos_sdk && { \
730+
boot=./tools/beken_packager/bootloader_bk7251_uart2_v1.0.15_enc.bin; \
731+
raw=./tools/beken_packager/bootloader_bk7251_uart2_v1.0.15.bin; \
732+
patched=./tools/beken_packager/bootloader_bk7251_uart2_v1.0.15_tuya_ota_patch.bin; \
733+
patched_enc=./tools/beken_packager/bootloader_bk7251_uart2_v1.0.15_tuya_ota_patch_enc.bin; \
734+
bak=$$boot.bk7252-tuya-patch; \
735+
cp "$$boot" "$$bak" || exit $$?; \
736+
status=0; \
737+
python ../../platforms/BK723x/patch_bk7252_tuya_bootloader.py "$$raw" "$$patched" "$(PWD)/sdk/beken_freertos_sdk/toolchain/arm-none-eabi/bin" ../../platforms/BK723x/bk7252_tuya_ota_write_wrap.c && \
738+
./tools/crc_binary/encrypt_n "$$patched" 510fb093 a3cbeadc 5993a17e c7adeb03 0 && \
739+
mv -f "$$patched_enc" "$$boot" && \
740+
./tools/rt_partition_tool/rt_partition_tool_cli-x64 "$$boot" ../../platforms/BK723x/bk7252_partition_2M.json && \
741+
ARM_GCC_TOOLCHAIN=$(PWD)/sdk/beken_freertos_sdk/toolchain/arm-none-eabi/bin/ OBK_VARIANT=$(OBK_VARIANT) sh build.sh bk7251 $(APP_VERSION) || status=$$?; \
742+
rm -f "$$patched" "$$patched_enc"; \
743+
mv -f "$$bak" "$$boot"; \
744+
exit $$status; \
745+
}
730746
mkdir -p output/$(APP_VERSION)
731747
cp sdk/beken_freertos_sdk/out/bk7251.bin output/$(APP_VERSION)/OpenBK7252_${APP_VERSION}.bin
732748
cp sdk/beken_freertos_sdk/out/bk7251_QIO.bin output/$(APP_VERSION)/OpenBK7252_QIO_${APP_VERSION}.bin
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"part_table": [
3+
{
4+
"name": "download",
5+
"flash_name": "beken_onchip",
6+
"offset": "0x00132000",
7+
"len": "696K"
8+
},
9+
{
10+
"name": "app",
11+
"flash_name": "beken_onchip_crc",
12+
"offset": "0x00010000",
13+
"len": "1088K"
14+
},
15+
{
16+
"name": "bootloader",
17+
"flash_name": "beken_onchip_crc",
18+
"offset": "0x00000000",
19+
"len": "64K"
20+
}
21+
]
22+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#include <stdint.h>
2+
3+
#define COEF0 0x510FB093u
4+
#define COEF1 0xA3CBEADCu
5+
#define COEF2 0x5993A17Eu
6+
#define COEF3 0xC7ADEB03u
7+
8+
typedef int (*fal_partition_write_fn)(void *part, uint32_t offset, const uint8_t *buf, uint32_t size);
9+
10+
static uint32_t rol32(uint32_t v, unsigned int bits)
11+
{
12+
return (v << bits) | (v >> (32u - bits));
13+
}
14+
15+
static uint32_t pn15(uint32_t v, uint32_t disabled)
16+
{
17+
uint32_t part1;
18+
uint32_t x;
19+
uint32_t part2;
20+
21+
if (disabled)
22+
return 0;
23+
24+
v &= 0xffffu;
25+
part1 = ((v << 9) + (v >> 7)) & 0xffffu;
26+
x = v >> 5;
27+
part2 = ((x << 12) + ((x & 0xfu) << 8) + ((x << 4) & 0xffu) + (x & 0xfu)) & 0x6371u;
28+
return part1 ^ part2;
29+
}
30+
31+
static uint32_t pn16(uint32_t v, uint32_t disabled)
32+
{
33+
uint32_t part1;
34+
uint32_t seed;
35+
uint32_t part2;
36+
37+
if (disabled)
38+
return 0;
39+
40+
v &= 0x1ffffu;
41+
part1 = (((v & 0x3ffu) << 7) + ((v >> 10) & 0x7fu)) & 0x1ffffu;
42+
seed = (((v >> 1) & 1u) << 3) + (((v >> 5) & 1u) << 2) + (((v >> 9) & 1u) << 1) + ((v >> 13) & 1u);
43+
part2 = ((((v >> 4) & 1u) << 16) + (seed << 12) + (seed << 8) + (seed << 4) + seed) & 0x13659u;
44+
return part1 ^ part2;
45+
}
46+
47+
static uint32_t pn32(uint32_t v, uint32_t disabled)
48+
{
49+
uint32_t seed;
50+
uint32_t part2;
51+
52+
if (disabled)
53+
return 0;
54+
55+
seed = (v >> 2) & 0xfu;
56+
part2 = (seed << 28) + (seed << 24) + (seed << 20) + (seed << 16) + (seed << 12) + (seed << 8) + (seed << 4) + seed;
57+
return rol32(v, 17) ^ (part2 & 0xE519A4F1u);
58+
}
59+
60+
static uint32_t enc_word(uint32_t addr, uint32_t data)
61+
{
62+
uint32_t all_disabled;
63+
uint32_t high_a;
64+
uint32_t high_b;
65+
uint32_t low_b;
66+
uint32_t pn15_input;
67+
uint32_t pn16_input;
68+
uint32_t pn32_input;
69+
uint32_t word_mode;
70+
uint32_t rotate_mode;
71+
uint32_t mask;
72+
73+
all_disabled = (((COEF3 & 0xff000000u) == 0xff000000u) || ((COEF3 & 0xff000000u) == 0));
74+
word_mode = (COEF3 >> 5) & 3u;
75+
rotate_mode = (COEF3 >> 11) & 3u;
76+
77+
high_a = (((addr >> 24) << 8) + ((addr >> 16) & 0xffu)) & 0xffffu;
78+
high_b = (((addr >> 16) << 8) + ((addr >> 24) & 0xffu)) & 0xffffu;
79+
low_b = ((addr << 8) + ((addr >> 8) & 0xffu)) & 0xffffu;
80+
81+
if (word_mode == 0)
82+
pn15_input = high_a ^ (addr & 0xffffu);
83+
else if (word_mode == 1)
84+
pn15_input = high_a ^ low_b;
85+
else if (word_mode == 2)
86+
pn15_input = high_b ^ (addr & 0xffffu);
87+
else
88+
pn15_input = high_b ^ low_b;
89+
90+
pn16_input = (addr >> ((COEF3 >> 8) & 3u)) & 0x1ffffu;
91+
if (rotate_mode == 0)
92+
pn32_input = addr;
93+
else if (rotate_mode == 1)
94+
pn32_input = rol32(addr, 24);
95+
else if (rotate_mode == 2)
96+
pn32_input = rol32(addr, 16);
97+
else
98+
pn32_input = rol32(addr, 8);
99+
100+
mask = (pn15(((COEF1 >> 16) ^ pn15_input) & 0xffffu, all_disabled || (COEF3 & 1u)) & 0xffffu) << 16;
101+
mask |= pn16(((((COEF1 >> 8) & 0xffu) << 9) + (((COEF3 >> 4) & 1u) << 8) + (COEF1 & 0xffu)) ^ pn16_input,
102+
all_disabled || (COEF3 & 2u)) & 0xffffu;
103+
mask ^= pn32(COEF0 ^ pn32_input, all_disabled || (COEF3 & 4u));
104+
if (!(all_disabled || (COEF3 & 8u)))
105+
mask ^= COEF2;
106+
107+
return data ^ mask;
108+
}
109+
110+
int tuya_fal_partition_write(void *part, uint32_t offset, uint8_t *buf, uint32_t size)
111+
{
112+
uint32_t padded_size;
113+
uint32_t addr;
114+
uint32_t i;
115+
116+
padded_size = (size + 31u) & ~31u;
117+
for (i = size; i < padded_size; i++)
118+
buf[i] = 0xffu;
119+
120+
addr = *(const uint32_t *)((const uint8_t *)part + 0x34u) + offset;
121+
for (i = 0; i < padded_size; i += 4) {
122+
uint32_t word = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8) | ((uint32_t)buf[i + 2] << 16) | ((uint32_t)buf[i + 3] << 24);
123+
word = enc_word(addr + i, word);
124+
buf[i] = (uint8_t)word;
125+
buf[i + 1] = (uint8_t)(word >> 8);
126+
buf[i + 2] = (uint8_t)(word >> 16);
127+
buf[i + 3] = (uint8_t)(word >> 24);
128+
}
129+
130+
return ((fal_partition_write_fn)0x7779u)(part, offset, buf, padded_size);
131+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#!/usr/bin/env python3
2+
import argparse
3+
import hashlib
4+
import pathlib
5+
import subprocess
6+
import tempfile
7+
8+
9+
RAW_BOOTLOADER_SHA256 = "6342d53961e743dfe73c852b31c7083f08f740fc27444cb75ebcb22d4b1031f8"
10+
WRAPPER_OFFSET = 0xD300
11+
ORIGINAL_WRITE = 0x7778
12+
CALL_SITES = (0x6774, 0x6816)
13+
14+
15+
def thumb_bl(site, target):
16+
offset = target - (site + 4)
17+
if offset & 1:
18+
raise ValueError("unaligned Thumb BL target")
19+
if not (-(1 << 22) <= offset < (1 << 22)):
20+
raise ValueError("Thumb BL target out of range")
21+
22+
imm = offset >> 1
23+
hi = 0xF000 | ((imm >> 11) & 0x7FF)
24+
lo = 0xF800 | (imm & 0x7FF)
25+
return hi.to_bytes(2, "little") + lo.to_bytes(2, "little")
26+
27+
28+
def tool_path(toolchain, name):
29+
path = toolchain / name
30+
if path.exists():
31+
return path
32+
33+
exe_path = toolchain / (name + ".exe")
34+
if exe_path.exists():
35+
return exe_path
36+
37+
return path
38+
39+
40+
def build_wrapper(toolchain, source):
41+
gcc = tool_path(toolchain, "arm-none-eabi-gcc")
42+
nm = tool_path(toolchain, "arm-none-eabi-nm")
43+
objcopy = tool_path(toolchain, "arm-none-eabi-objcopy")
44+
45+
with tempfile.TemporaryDirectory(prefix="bk7252_tuya_bl_") as tmp:
46+
tmp_path = pathlib.Path(tmp)
47+
obj = tmp_path / "wrap.o"
48+
elf = tmp_path / "wrap.elf"
49+
binary = tmp_path / "wrap.bin"
50+
51+
subprocess.run([
52+
str(gcc), "-c", str(source), "-o", str(obj),
53+
"-Os", "-mthumb", "-mcpu=arm9tdmi",
54+
"-ffreestanding", "-fno-builtin", "-fno-pic",
55+
"-fno-unwind-tables", "-fno-asynchronous-unwind-tables",
56+
], check=True)
57+
subprocess.run([
58+
str(gcc), "-nostdlib", "-Wl,-Ttext=0x%08x" % WRAPPER_OFFSET,
59+
"-Wl,-e,tuya_fal_partition_write",
60+
"-o", str(elf), str(obj),
61+
], check=True)
62+
nm_output = subprocess.check_output([str(nm), "-n", str(elf)], text=True)
63+
for line in nm_output.splitlines():
64+
parts = line.split()
65+
if len(parts) >= 3 and parts[2] == "tuya_fal_partition_write":
66+
entry = int(parts[0], 16)
67+
if entry != WRAPPER_OFFSET:
68+
raise RuntimeError("wrapper entry at 0x%04x, expected 0x%04x" % (entry, WRAPPER_OFFSET))
69+
break
70+
else:
71+
raise RuntimeError("wrapper entry symbol not found")
72+
73+
subprocess.run([
74+
str(objcopy), "-O", "binary", "-j", ".text", str(elf), str(binary),
75+
], check=True)
76+
77+
return binary.read_bytes()
78+
79+
80+
def patch_bootloader(raw_path, output_path, wrapper):
81+
raw = bytearray(raw_path.read_bytes())
82+
raw_sha = hashlib.sha256(raw).hexdigest()
83+
if raw_sha != RAW_BOOTLOADER_SHA256:
84+
raise RuntimeError("unexpected BK7252 bootloader SHA256: %s" % raw_sha)
85+
86+
for site in CALL_SITES:
87+
expected = thumb_bl(site, ORIGINAL_WRITE)
88+
found = bytes(raw[site:site + 4])
89+
if found != expected:
90+
raise RuntimeError("unexpected call at 0x%04x: got %s expected %s" % (site, found.hex(), expected.hex()))
91+
raw[site:site + 4] = thumb_bl(site, WRAPPER_OFFSET)
92+
93+
wrapper_end = WRAPPER_OFFSET + len(wrapper)
94+
if len(raw) > WRAPPER_OFFSET:
95+
raise RuntimeError("wrapper offset overlaps bootloader body")
96+
if wrapper_end > 0x10000:
97+
raise RuntimeError("patched bootloader exceeds 64K logical boot partition")
98+
99+
raw.extend(b"\xff" * (WRAPPER_OFFSET - len(raw)))
100+
raw[WRAPPER_OFFSET:wrapper_end] = wrapper
101+
output_path.write_bytes(raw)
102+
103+
print("Patched BK7252 Tuya bootloader: wrapper %d bytes at 0x%04x" % (len(wrapper), WRAPPER_OFFSET))
104+
105+
106+
def main():
107+
parser = argparse.ArgumentParser(description="Patch the BK7252 RT bootloader to coeff-encrypt app writes for Tuya OTA.")
108+
parser.add_argument("raw_bootloader", type=pathlib.Path)
109+
parser.add_argument("output_bootloader", type=pathlib.Path)
110+
parser.add_argument("toolchain_bin", type=pathlib.Path)
111+
parser.add_argument("wrapper_source", type=pathlib.Path)
112+
args = parser.parse_args()
113+
114+
wrapper = build_wrapper(args.toolchain_bin, args.wrapper_source)
115+
patch_bootloader(args.raw_bootloader, args.output_bootloader, wrapper)
116+
117+
118+
if __name__ == "__main__":
119+
main()

0 commit comments

Comments
 (0)