Skip to content

Commit 5b21cdd

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 5b21cdd

File tree

12 files changed

+311
-20
lines changed

12 files changed

+311
-20
lines changed

docs/fw_binaries.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,3 +289,100 @@ extract the FSBL and PMUFW from the complete boot image.
289289
configuration:
290290
* path
291291

292+
## For Rockchip devices
293+
294+
While it is possible to use the DDR init files and SPL files from Rockchip,
295+
it is easier to use the files generated by u-boot as support for the
296+
Rokchip USB protocol is not implemented.
297+
Nevertheless, this means that to boot u-boot proper, u-boot has to be
298+
patched to get support for:
299+
300+
- boot from RAM,
301+
- boot from SPL DFU
302+
303+
The patches have been sent to the u-boot mailing list by their authors
304+
and not yet merged. The patches can all be found inside this
305+
[merge request](https://gitlab.collabora.com/hardware-enablement/rockchip-3588/u-boot/-/merge_requests/5).
306+
307+
308+
309+
### Using binary generated by Rockchip's ``boot_merger``
310+
311+
[example](../src/snagrecover/templates/rockchip-merger.yaml)
312+
313+
Snagboot can upload the ``CODE471_OPTION`` and ``CODE472_OPTION`` of a binary
314+
generated with the ``boot_merger`` tool and configuration files from
315+
[Rockchip rkbin](https://github.com/radxa/rkbin/tree/develop-v2024.03/).
316+
317+
In case of u-boot, this would mean to upload the TPL, SPL and then use SPL DFU
318+
to boot u-boot proper (see later section for SPL DFU).
319+
320+
An example configuration for rk3399 would be:
321+
```
322+
[CHIP_NAME]
323+
NAME=RK330C
324+
[VERSION]
325+
MAJOR=1
326+
MINOR=123
327+
[CODE471_OPTION]
328+
NUM=1
329+
Path1=tpl/u-boot-tpl-dtb.bin
330+
Sleep=1
331+
[CODE472_OPTION]
332+
NUM=1
333+
Path1=spl/u-boot-spl-dtb.bin
334+
Sleep=3000
335+
[LOADER_OPTION]
336+
NUM=2
337+
LOADER1=FlashData
338+
LOADER2=FlashBoot
339+
FlashData=spl/u-boot-spl-dtb.bin
340+
FlashBoot=u-boot.itb
341+
[OUTPUT]
342+
PATH=rk3399_uboot.bin
343+
[FLAG]
344+
471_RC4_OFF=false
345+
```
346+
347+
The ``tpl/u-boot-tpl-dtb.bin``, ``spl/u-boot-spl-dtb.bin``, ``u-boot.itb``
348+
files are generated during u-boot's build. Please note has the ``LOADER_OPTION``
349+
is not handled by snagboot.
350+
351+
352+
### Boot from Ram
353+
354+
[example](../src/snagrecover/templates/rockchip-ramboot.yaml)
355+
356+
When building u-boot with the previously mentioned patches, u-boot will generate two files:
357+
358+
- ``u-boot-rockchip-usb471.bin``
359+
- ``u-boot-rockchip-usb472.bin``
360+
361+
These are the files needed to boot from RAM.
362+
On platforms like rk3588 using the TPL from rockchip, some issues will arrive
363+
when booting. It's due to the ``u-boot-rockchip-usb472.bin`` binary overwriting
364+
the ATAGS provided by the TPL. A possible fix would be to patch u-boot with
365+
https://github.com/u-boot/u-boot/commit/a6e85a35b50ade7df5f32092c1cc05ade303a22a.
366+
367+
368+
### Boot from SPL DFU
369+
370+
[example](../src/snagrecover/templates/rockchip-spl-dfu.yaml)
371+
372+
To enable u-boot with SPL DFU support in the u-boot configuration, ensure that the
373+
following options are enabled:
374+
375+
```
376+
CONFIG_SPL_DM_USB_GADGET=y
377+
CONFIG_SPL_USB_GADGET=y
378+
CONFIG_SPL_DFU=y
379+
```
380+
381+
The (SPL) USB gadget driver needs to be enabled too.
382+
383+
At the end of the build, the following files will be needed:
384+
385+
- ``mkimage-in-simple-bin.mkimage-u-boot-tpl`` or ``mkimage-in-simple-bin.mkimage-rockchip-tpl``
386+
- ``mkimage-in-simple-bin.mkimage-u-boot-spl``
387+
- ``u-boot.itb``
388+

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)

src/snagrecover/protocols/rockchip.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
logger = logging.getLogger("snagrecover")
1515

16+
USB_REQUEST_TYPE_VENDOR = (0x02 << 5)
1617

1718
class RochipBootRomError(Exception):
1819
pass
@@ -43,13 +44,13 @@ def __init__(self, dev: usb.core.Device):
4344
self.ep_in = ep_in
4445
self.ep_out = ep_out
4546

46-
def __write_chunk(self, addr: int, chunk: bytes) -> bool:
47+
def __write_chunk(self, code: int, chunk: bytes) -> bool:
4748
logger.debug(f"Sending {len(chunk)} bytes")
48-
return self.dev.ctrl_transfer(0x40, 0x0c, 0, addr, chunk)
49+
return self.dev.ctrl_transfer(USB_REQUEST_TYPE_VENDOR, 0x0c, 0, code, chunk)
4950

50-
def write_blob(self, blob: bytes, addr: int) -> bool:
51-
if addr != 0x471 and addr != 0x472:
52-
raise RochipBootRomError("Invalid address. Can only be 471 or 472")
51+
def write_blob(self, blob: bytes, code: int) -> bool:
52+
if code != 0x471 and code != 0x472:
53+
raise RochipBootRomError("Invalid code value. Can only be 0x471 or 0x472")
5354
crc = Crc16Ibm3740()
5455
total_written = 0
5556
crc_sent = False
@@ -67,14 +68,14 @@ def write_blob(self, blob: bytes, addr: int) -> bool:
6768
chunk.append(crcbytes >> 8)
6869
chunk.append(crcbytes & 0xff)
6970
crc_sent = True
70-
written = self.__write_chunk(addr, chunk)
71+
written = self.__write_chunk(code, chunk)
7172
ret = written == len(chunk)
7273
if ret is False:
7374
return ret
7475
total_written += written
7576
if chunk_len+2 == 4096:
7677
chunk = [0x00]
77-
written = self.__write_chunk(addr, chunk)
78+
written = self.__write_chunk(code, chunk)
7879
ret = written == len(chunk)
7980
if ret is False:
8081
return ret
@@ -83,7 +84,7 @@ def write_blob(self, blob: bytes, addr: int) -> bool:
8384
crcbytes = crc.final()
8485
chunk.append(crcbytes >> 8)
8586
chunk.append(crcbytes & 0xff)
86-
written = self.__write_chunk(addr, chunk)
87+
written = self.__write_chunk(code, chunk)
8788
ret = written == len(chunk)
8889
if ret is False:
8990
return ret

0 commit comments

Comments
 (0)