Skip to content

Commit f50c7d3

Browse files
committed
snagrecover: Add rockchip support
Add support for booting a rockchip bin file, made with boot_merger. It can be: - a file containing the rockchip miniloader, but in this case, it's not useful, as rockusb is not implemented - a file containing uboot tpl and spl. In this case, if u-boot is configured for SPL DFU, the snagrecover configuration file may specify an u-boot FIT image to load into the first DFU slot. This will allow booting u-boot proper and then to fastboot/ums/dfu/... to flash. Signed-off-by: Arnaud Patard <[email protected]>
1 parent 8fcddbc commit f50c7d3

File tree

9 files changed

+194
-12
lines changed

9 files changed

+194
-12
lines changed

src/snagrecover/50-snagboot.rules

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,21 @@ SUBSYSTEM=="usb", ATTRS{idVendor}=="0451", ATTRS{idProduct}=="d022", MODE="0660"
7676

7777
#Xilinx rules
7878
SUBSYSTEM=="usb", ATTRS{idVendor}=="03fd", ATTRS{idProduct}=="0050", MODE="0660", TAG+="uaccess"
79+
80+
#Rockchip systems
81+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="2207", MODE="0660", TAG+="uaccess"
82+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="110a", MODE="0660", TAG+="uaccess"
83+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="110b", MODE="0660", TAG+="uaccess"
84+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="300a", MODE="0660", TAG+="uaccess"
85+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="301a", MODE="0660", TAG+="uaccess"
86+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="310b", MODE="0660", TAG+="uaccess"
87+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="310c", MODE="0660", TAG+="uaccess"
88+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="320a", MODE="0660", TAG+="uaccess"
89+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="320b", MODE="0660", TAG+="uaccess"
90+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="320c", MODE="0660", TAG+="uaccess"
91+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="330a", MODE="0660", TAG+="uaccess"
92+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="330c", MODE="0660", TAG+="uaccess"
93+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="330d", MODE="0660", TAG+="uaccess"
94+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="330e", MODE="0660", TAG+="uaccess"
95+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="350a", MODE="0660", TAG+="uaccess"
96+
SUBSYSTEM=="usb", ATTRS{idVendor}=="2207", ATTRS{idProduct}=="350b", MODE="0660", TAG+="uaccess"

src/snagrecover/config.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,23 @@
5252
"imx8mm": "1fc9:0134",
5353
"imx8mq": "1fc9:012b",
5454
"imx53" : "15a2:004e",
55+
},
56+
"rockchip": {
57+
"rv1108": "2207:110a",
58+
"rv1126": "2207:110b",
59+
"rk3066": "2207:300a",
60+
"rk3036": "2207:301a",
61+
"rk3188": "2207:310b",
62+
"rk3128": "2207:310c",
63+
"rk3288": "2207:320a",
64+
"rk322x": "2207:320b",
65+
"rk3328": "2207:320c",
66+
"rk3368": "2207:330a",
67+
"rk3399": "2207:330c",
68+
"px30": "2207:330d",
69+
"rk3308": "2207:330e",
70+
"rk3568": "2207:350a",
71+
"rk3588": "2207:350b"
5572
}
5673
}
5774

@@ -80,6 +97,8 @@ def init_config(args: list):
8097
if args.usb_path is None:
8198
if soc_family == "imx":
8299
usb_ids = default_usb_ids["imx"][soc_model]
100+
elif soc_family == "rockchip":
101+
usb_ids = default_usb_ids["rockchip"][soc_model]
83102
else:
84103
usb_ids = default_usb_ids[soc_family]
85104

src/snagrecover/firmware/firmware.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ def run_firmware(port, fw_name: str, subfw_name: str = ""):
146146
elif soc_family == "zynqmp":
147147
from snagrecover.firmware.zynqmp_fw import zynqmp_run
148148
zynqmp_run(port, fw_name, fw_blob, subfw_name)
149+
elif soc_family == "rockchip":
150+
from snagrecover.firmware.rk_fw import rockchip_run
151+
rockchip_run(port, fw_name, fw_blob)
149152
else:
150153
raise Exception(f"Unsupported SoC family {soc_family}")
151154
logger.info(f"Done installing firmware {fw_name}")

src/snagrecover/firmware/rk_fw.py

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,29 @@
1212
import logging
1313
import struct
1414
import time
15-
from cryptography.hazmat.primitives.ciphers import Cipher
16-
from cryptography.hazmat.decrepit.ciphers.algorithms import ARC4
1715
from crccheck import crc
1816
from dataclasses import dataclass
1917

2018
logger = logging.getLogger("snagrecover")
2119
from snagrecover.protocols import rockchip
20+
from snagrecover.protocols import dfu
2221
from snagrecover.utils import BinFileHeader
2322
from snagrecover.config import recovery_config
2423

24+
2525
# List generated with a grep on rkbin repository
26-
NEWIDB_LIST = [ "rk3506", "rk3506b", "rk3528", "rk3562", "rk3566", "rk3568", "rk3576", "rk3583", "rk3588", "rv1103b", "rv1106" ]
26+
NEWIDB_LIST = ["rk3506", "rk3506b", "rk3528", "rk3562", "rk3566", "rk3568", "rk3576", "rk3583", "rk3588", "rv1103b", "rv1106"]
2727

2828
BOOTTAG = b"BOOT"
2929
LDRTAG = b"LDR "
30-
TAG_LIST = [ BOOTTAG, LDRTAG ]
30+
TAG_LIST = [BOOTTAG, LDRTAG]
3131
BOOTENTRYSIZE = 57
3232
BOOTHEADERENTRYSIZE = 6
3333
BOOTHEADERSIZE = 102
3434
BOOTHEADERTIMESIZE = 7
3535
RC4_KEY = bytearray([124, 78, 3, 4, 85, 5, 9, 7, 45, 44, 123, 56, 23, 13, 23, 17])
3636

37+
3738
@dataclass
3839
class BootEntry(BinFileHeader):
3940
size: int
@@ -76,6 +77,7 @@ class BootReleaseTime(BinFileHeader):
7677
def __str__(self):
7778
return f"{self.year}/{self.month}/{self.day} {self.hour}:{self.minute}:{self.second}"
7879

80+
7981
class LoaderFileError(Exception):
8082
def __init__(self, message):
8183
self.message = message
@@ -84,6 +86,7 @@ def __init__(self, message):
8486
def __str__(self):
8587
return f"File format error: {self.message}"
8688

89+
8790
@dataclass
8891
class BootHeader(BinFileHeader):
8992
tag: bytes
@@ -105,7 +108,7 @@ class BootHeader(BinFileHeader):
105108

106109
def __post_init__(self):
107110
if self.tag not in TAG_LIST:
108-
raise LoaderFileError(f"Invalid tag {self.header.tag}")
111+
raise LoaderFileError(f"Invalid tag {self.tag}")
109112
# not sure how to exactly parse version/merge_version
110113
self.maj_ver = self.version >> 8
111114
self.min_ver = self.version & 0xff
@@ -128,6 +131,7 @@ def __post_init__(self):
128131
def __str__(self):
129132
return f"{self.tag}, {self.size} ,{self.maj_ver}.{self.min_ver}, 0x{self.merge_version:0x}, {self.releasetime}, {self.chip}, {self.entry471}, {self.entry472}, {self.loader}, sign: {self.sign}, enc: {self.rc4}"
130133

134+
131135
class RkCrc32(crc.Crc32Base):
132136
"""CRC-32/ROCKCHIP
133137
"""
@@ -139,6 +143,7 @@ class RkCrc32(crc.Crc32Base):
139143
_reflect_output = False
140144
_xor_output = 0
141145

146+
142147
class LoaderFile():
143148
def __init__(self, blob):
144149
self.blob = blob
@@ -170,7 +175,7 @@ def __init__(self, blob):
170175
(self.crc32,) = struct.unpack("<I", crc32)
171176
assert self.crc32 == calc_crc32
172177

173-
def entry_data(self, name, idx = 0):
178+
def entry_data(self, name, idx=0):
174179
entry = None
175180
if name == "471":
176181
entry = self.entry471
@@ -190,6 +195,38 @@ def entry_data(self, name, idx = 0):
190195
def __str__(self):
191196
return f"{self.header} crc: {self.crc32:02x}"
192197

198+
199+
# Manual implementation of RC4, from Wikipedia's page
200+
# Not very secure, so don't use it elsewhere.
201+
class rc4():
202+
def __init__(self):
203+
self.S = list(range(256))
204+
self.i = 0
205+
self.j = 0
206+
207+
def ksa(self, key):
208+
keylength = len(key)
209+
self.S = list(range(256))
210+
j = 0
211+
for i in range(256):
212+
j = (j + self.S[i] + key[i % keylength]) % 256
213+
self.S[i], self.S[j] = self.S[j], self.S[i]
214+
215+
def prga(self):
216+
self.i = (self.i + 1) % 256
217+
self.j = (self.j + self.S[self.i]) % 256
218+
self.S[self.i], self.S[self.j] = self.S[self.j], self.S[self.i]
219+
K = self.S[(self.S[self.i] + self.S[self.j]) % 256]
220+
return K
221+
222+
def encrypt(self, buf):
223+
obuf = bytearray(len(buf))
224+
225+
for offset in list(range(len(buf))):
226+
obuf[offset] = buf[offset] ^ self.prga()
227+
return obuf
228+
229+
193230
def rc4_encrypt(fw_blob):
194231

195232
soc_model = recovery_config["soc_model"]
@@ -201,28 +238,39 @@ def rc4_encrypt(fw_blob):
201238
padded_len = (blob_len+4095)//4096 * 4096
202239
fw_blob = bytearray(fw_blob)
203240
fw_blob += bytearray([0]*(padded_len - blob_len))
204-
a = ARC4(RC4_KEY)
205-
c = Cipher(a, mode=None)
206-
d = c.encryptor()
241+
encoder = rc4()
242+
encoder.ksa(RC4_KEY)
207243
obuf = bytearray()
208244
for i in range(padded_len):
209-
obuf += d.update(fw_blob[i*512:(i+1)*512])
245+
obuf += encoder.encrypt(fw_blob[i*512:(i+1)*512])
210246
return obuf
211247

248+
212249
def rockchip_run(dev, fw_name, fw_blob):
213-
rom = rockchip.RochipBootRom(dev)
214250

215251
if fw_name == 'code471':
216252
logger.info("Downloading code471...")
253+
rom = rockchip.RochipBootRom(dev)
217254
blob = rc4_encrypt(fw_blob)
218255
rom.write_blob(blob, 0x471)
219256
elif fw_name == 'code472':
220257
logger.info("Downloading code472...")
258+
rom = rockchip.RochipBootRom(dev)
221259
blob = rc4_encrypt(fw_blob)
222260
rom.write_blob(blob, 0x472)
261+
elif fw_name == "u-boot-fit":
262+
id = dfu.search_partid(dev, "u-boot.itb")
263+
if id is None:
264+
logger.error("Missing u-boot.itb DFU partition")
265+
dfu_cmd = dfu.DFU(dev, stm32=False)
266+
dfu_cmd.get_status()
267+
dfu_cmd.download_and_run(fw_blob, id, 0, len(fw_blob))
268+
dfu_cmd.get_status()
269+
dfu_cmd.detach(id)
223270
else:
224271
fw = LoaderFile(fw_blob)
225272
logger.info(f"{fw}")
273+
rom = rockchip.RochipBootRom(dev)
226274
for i in range(fw.header.entry471.count):
227275
logger.info(f"Downloading entry 471 {i}...")
228276
(data, delay) = fw.entry_data("471", i)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import usb
2+
import logging
3+
logger = logging.getLogger("snagrecover")
4+
from snagrecover.firmware.firmware import run_firmware
5+
from snagrecover.utils import get_usb
6+
from snagrecover.utils import cli_error
7+
from snagrecover.config import recovery_config
8+
import time
9+
10+
11+
def main():
12+
usb_addr = recovery_config["usb_path"]
13+
dev = get_usb(usb_addr)
14+
15+
# Blob made with boot_merger
16+
if "xpl" in recovery_config["firmware"]:
17+
try:
18+
run_firmware(dev, "xpl")
19+
usb.util.dispose_resources(dev)
20+
except Exception as e:
21+
cli_error(f"Failed to run firmware: {e}")
22+
23+
# u-boot binaries.
24+
elif "code471" in recovery_config["firmware"] and "code472" in recovery_config["firmware"]:
25+
try:
26+
run_firmware(dev, "code471")
27+
usb.util.dispose_resources(dev)
28+
except Exception as e:
29+
cli_error(f"Failed to run code471 firmware: {e}")
30+
if "delay" in recovery_config["firmware"]["code471"]:
31+
delay = recovery_config["firmware"]["code471"]["delay"]
32+
logger.info(f"Sleeping {delay}ms")
33+
time.sleep(delay / 1000)
34+
try:
35+
run_firmware(dev, "code472")
36+
usb.util.dispose_resources(dev)
37+
except Exception as e:
38+
cli_error(f"Failed to run code472 firmware: {e}")
39+
if "delay" in recovery_config["firmware"]["code472"]:
40+
delay = recovery_config["firmware"]["code472"]["delay"]
41+
logger.info(f"Sleeping {delay}ms")
42+
time.sleep(delay / 1000)
43+
else:
44+
cli_error("Missing xpl or code471/code472 binary configuration")
45+
46+
if "u-boot-fit" in recovery_config["firmware"]:
47+
try:
48+
dev = get_usb(usb_addr)
49+
run_firmware(dev, "u-boot-fit")
50+
except Exception as e:
51+
cli_error(f"Failed to load u-boot.itb: {e}")

src/snagrecover/supported_socs.yaml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ tested:
5757
family: stm32mp
5858
zynqmp:
5959
family: zynqmp
60+
rk3399:
61+
family: rockchip
62+
rk3588:
63+
family: rockchip
6064
untested:
6165
a10:
6266
family: sunxi
@@ -144,4 +148,29 @@ untested:
144148
family: sunxi
145149
v853:
146150
family: sunxi
147-
151+
rv1108:
152+
family: rockchip
153+
rv1126:
154+
family: rockchip
155+
rk3066:
156+
family: rockchip
157+
rk3036:
158+
family: rockchip
159+
rk3188:
160+
family: rockchip
161+
rk3128:
162+
family: rockchip
163+
rk3288:
164+
family: rockchip
165+
rk322x:
166+
family: rockchip
167+
rk3328:
168+
family: rockchip
169+
rk3368:
170+
family: rockchip
171+
px30:
172+
family: rockchip
173+
rk3308:
174+
family: rockchip
175+
rk3568:
176+
family: rockchip
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
xpl:
2+
# u-boot TPL+SPL with SPL configured for DFU boot
3+
path: rk3399_uboot.bin
4+
u-boot-fit:
5+
# proper u-boot
6+
path: u-boot.itb
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
code471:
2+
path: u-boot-rockchip-usb471.bin
3+
delay: 1
4+
code472:
5+
path: u-boot-rockchip-usb472.bin

src/snagrecover/utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ def get_recovery(soc_family: str):
191191
elif soc_family == "zynqmp":
192192
from snagrecover.recoveries.zynqmp import main as zynqmp_recovery
193193
return zynqmp_recovery
194+
elif soc_family == "rockchip":
195+
from snagrecover.recoveries.rockchip import main as rockchip_recovery
196+
return rockchip_recovery
194197
else:
195198
cli_error(f"unsupported board family {soc_family}")
196199

0 commit comments

Comments
 (0)