Skip to content

Commit 8adcd1e

Browse files
committed
driver: add RKUSBMaskromDriver bootstrap driver
Add a RKUSBMaskromDriver bootstrap driver with an accompanying rkusbmaskrom agent to support bootstrapping targets with Rockchip SoCs. The rkusbmaskrom agent expects the target to be in MASKROM mode and send images to BootROM using vendor specific 0x471 and 0x472 control transfers, in 4 KiB chunks. The first image is loaded to SRAM using 0x471 and is expected to initialize DRAM and then return back to BootROM. A second image can then be loaded to start of DRAM using 0x472. Signed-off-by: Jonas Karlman <[email protected]>
1 parent ae2f2a5 commit 8adcd1e

File tree

4 files changed

+225
-1
lines changed

4 files changed

+225
-1
lines changed

doc/configuration.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,7 @@ Arguments:
784784

785785
Used by:
786786
- `RKUSBDriver`_
787+
- `RKUSBMaskromDriver`_
787788

788789
NetworkMXSUSBLoader
789790
~~~~~~~~~~~~~~~~~~~
@@ -2638,6 +2639,39 @@ Arguments:
26382639
- usb_loader (str): optional, key in :ref:`images <labgrid-device-config-images>` containing the path
26392640
of a first-stage bootloader image to write
26402641

2642+
RKUSBMaskromDriver
2643+
~~~~~~~~~~~~~~~~~~
2644+
An :any:`RKUSBMaskromDriver` is used to upload an image into a device in the
2645+
*Rockchip USB Maskrom state*.
2646+
This is useful to bootstrap a bootloader onto a device.
2647+
2648+
Binds to:
2649+
loader:
2650+
- `RKUSBLoader`_
2651+
- `NetworkRKUSBLoader`_
2652+
2653+
Implements:
2654+
- :any:`BootstrapProtocol`
2655+
2656+
.. code-block:: yaml
2657+
2658+
targets:
2659+
main:
2660+
drivers:
2661+
RKUSBMaskromDriver:
2662+
initial: 'rkmaskrom471'
2663+
image: 'rkmaskrom472'
2664+
2665+
images:
2666+
rkmaskrom471: 'path/to/u-boot-rockchip-usb471.bin'
2667+
rkmaskrom472: 'path/to/u-boot-rockchip-usb472.bin'
2668+
2669+
Arguments:
2670+
- initial (str): optional, key in :ref:`images <labgrid-device-config-images>` containing the path
2671+
of an image that typically initializes DRAM of the target
2672+
- image (str): optional, key in :ref:`images <labgrid-device-config-images>` containing the path
2673+
of a bootloader image to load to start of DRAM, or to SRAM when an initial image is unused
2674+
26412675
UUUDriver
26422676
~~~~~~~~~
26432677
A :any:`UUUDriver` is used to upload an image into a device in the *NXP USB

labgrid/driver/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
DigitalOutputPowerDriver, YKUSHPowerDriver, \
1717
USBPowerDriver, SiSPMPowerDriver, NetworkPowerDriver, \
1818
PDUDaemonDriver
19-
from .usbloader import MXSUSBDriver, IMXUSBDriver, BDIMXUSBDriver, RKUSBDriver, UUUDriver
19+
from .usbloader import MXSUSBDriver, IMXUSBDriver, BDIMXUSBDriver, RKUSBDriver, \
20+
RKUSBMaskromDriver, UUUDriver
2021
from .usbsdmuxdriver import USBSDMuxDriver
2122
from .usbsdwiredriver import USBSDWireDriver
2223
from .common import Driver

labgrid/driver/usbloader.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33

44
from ..factory import target_factory
55
from ..protocol import BootstrapProtocol
6+
from ..resource.remote import NetworkRKUSBLoader
67
from ..step import step
78
from .common import Driver
9+
from ..util.agentwrapper import AgentWrapper
810
from ..util.managedfile import ManagedFile
911
from ..util.timeout import Timeout
1012
from ..util.helper import processwrapper
@@ -149,6 +151,53 @@ def load(self, filename=None):
149151
raise
150152

151153

154+
@target_factory.reg_driver
155+
@attr.s(eq=False)
156+
class RKUSBMaskromDriver(Driver, BootstrapProtocol):
157+
bindings = {"loader": {"RKUSBLoader", NetworkRKUSBLoader}}
158+
initial = attr.ib(default=None)
159+
image = attr.ib(default=None)
160+
delay = attr.ib(default=0.0, validator=attr.validators.instance_of(float))
161+
162+
def __attrs_post_init__(self):
163+
super().__attrs_post_init__()
164+
self.wrapper = None
165+
166+
def on_activate(self):
167+
if isinstance(self.loader, NetworkRKUSBLoader):
168+
host = self.loader.host
169+
else:
170+
host = None
171+
self.wrapper = AgentWrapper(host)
172+
self.proxy = self.wrapper.load('rkusbmaskrom')
173+
174+
def on_deactivate(self):
175+
self.proxy = None
176+
if self.wrapper:
177+
self.wrapper.close()
178+
self.wrapper = None
179+
180+
@Driver.check_active
181+
@step(args=['filename'])
182+
def load(self, filename=None):
183+
images = []
184+
if self.initial is not None:
185+
images.append(self.target.env.config.get_image_path(self.initial))
186+
if not filename and self.image is not None:
187+
filename = self.target.env.config.get_image_path(self.image)
188+
if filename:
189+
images.append(filename)
190+
if not images:
191+
raise Exception("No images to load")
192+
193+
args = [self.loader.busnum, self.loader.devnum]
194+
for path in images:
195+
mf = ManagedFile(path, self.loader)
196+
mf.sync_to_resource()
197+
args.append(mf.get_remote_path())
198+
self.proxy.load(*args, delay=self.delay)
199+
200+
152201
@target_factory.reg_driver
153202
@attr.s(eq=False)
154203
class UUUDriver(Driver, BootstrapProtocol):

labgrid/util/agents/rkusbmaskrom.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"""
2+
This module implements the communication protocol to load an image to SRAM,
3+
that typically initializes DRAM, followed by optionally loading a secondary
4+
image to start of DRAM, when a Rockchip device is in MASKROM mode.
5+
"""
6+
from time import sleep
7+
8+
import usb.core
9+
import usb.util
10+
11+
12+
RK_RC4_KEY = [
13+
0x7c, 0x4e, 0x03, 0x04, 0x55, 0x05, 0x09, 0x07,
14+
0x2d, 0x2c, 0x7b, 0x38, 0x17, 0x0d, 0x17, 0x11,
15+
]
16+
17+
18+
CRC16_TABLE = [
19+
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
20+
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
21+
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
22+
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
23+
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
24+
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
25+
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
26+
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
27+
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
28+
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
29+
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
30+
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
31+
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
32+
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
33+
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
34+
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
35+
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
36+
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
37+
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
38+
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
39+
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
40+
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
41+
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
42+
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
43+
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
44+
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
45+
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
46+
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
47+
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
48+
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
49+
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
50+
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
51+
]
52+
53+
54+
def crc16(data, crc=0xffff):
55+
for byte in data:
56+
crc = ((crc << 8) & 0xff00) ^ CRC16_TABLE[((crc >> 8) & 0xff) ^ byte]
57+
return crc & 0xffff
58+
59+
60+
def rc4_ksa(key):
61+
keylength = len(key)
62+
S = list(range(256))
63+
j = 0
64+
for i in range(256):
65+
j = (j + S[i] + key[i % keylength]) % 256
66+
S[i], S[j] = S[j], S[i]
67+
return S
68+
69+
70+
def rc4_prga(S):
71+
i = 0
72+
j = 0
73+
while True:
74+
i = (i + 1) % 256
75+
j = (j + S[i]) % 256
76+
S[i], S[j] = S[j], S[i]
77+
K = S[(S[i] + S[j]) % 256]
78+
yield K
79+
80+
81+
class RKUSBMaskrom:
82+
def __init__(self, **args):
83+
self._dev = usb.core.find(**args)
84+
if self._dev is None:
85+
raise ValueError("Device not found")
86+
if self._dev.idVendor != 0x2207:
87+
raise ValueError(f"Unsupported device VID {self._dev.idVendor:x}")
88+
if self._dev.bcdUSB & 0x0001:
89+
raise ValueError("Device in LOADER mode")
90+
91+
if self._dev.is_kernel_driver_active(0):
92+
self._dev.detach_kernel_driver(0)
93+
94+
usb.util.claim_interface(self._dev, 0)
95+
96+
def __del__(self):
97+
usb.util.release_interface(self._dev, 0)
98+
usb.util.dispose_resources(self._dev)
99+
100+
def load(self, code, path):
101+
with open(path, 'rb') as fh:
102+
data = bytearray(fh.read())
103+
104+
# encrypt data using the known rockchip key for older devices
105+
if self._dev.idProduct < 0x3500:
106+
keystream = rc4_prga(rc4_ksa(RK_RC4_KEY))
107+
data = bytearray([byte ^ next(keystream) for byte in data])
108+
109+
# ensure crc16 fit in the last chunk
110+
if len(data) % 4096 == 4095:
111+
data.append(0)
112+
113+
# append crc16 of data
114+
crc = crc16(data)
115+
data.append(crc >> 8)
116+
data.append(crc & 0xff)
117+
118+
# extra chunk to signal end of transfer
119+
if len(data) % 4096 == 0:
120+
data.append(0)
121+
122+
# transfer all chunks
123+
for i in range(0, len(data), 4096):
124+
chunk = data[i:i + 4096]
125+
self._dev.ctrl_transfer(64, 12, 0, code, chunk, 5000)
126+
127+
128+
def handle_load(busnum, devnum, initial, secondary=None, delay=None):
129+
maskrom = RKUSBMaskrom(bus=busnum, address=devnum)
130+
maskrom.load(0x471, initial)
131+
if secondary is not None:
132+
if delay is not None:
133+
sleep(delay)
134+
maskrom.load(0x472, secondary)
135+
del maskrom
136+
137+
138+
methods = {
139+
"load": handle_load,
140+
}

0 commit comments

Comments
 (0)