diff --git a/doc/configuration.rst b/doc/configuration.rst index d93e58c90..21f2b1576 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -784,6 +784,7 @@ Arguments: Used by: - `RKUSBDriver`_ + - `RKUSBMaskromDriver`_ NetworkMXSUSBLoader ~~~~~~~~~~~~~~~~~~~ @@ -2638,6 +2639,40 @@ Arguments: - usb_loader (str): optional, key in :ref:`images ` containing the path of a first-stage bootloader image to write +RKUSBMaskromDriver +~~~~~~~~~~~~~~~~~~ +An :any:`RKUSBMaskromDriver` is used to upload an image into a device in the +*Rockchip USB Maskrom state*. +This is useful to bootstrap a bootloader onto a device. + +Binds to: + loader: + - `RKUSBLoader`_ + - `NetworkRKUSBLoader`_ + +Implements: + - :any:`BootstrapProtocol` + +.. code-block:: yaml + + targets: + main: + drivers: + RKUSBMaskromDriver: + initial: 'rkmaskrom471' + image: 'rkmaskrom472' + + images: + rkmaskrom471: 'path/to/u-boot-rockchip-usb471.bin' + rkmaskrom472: 'path/to/u-boot-rockchip-usb472.bin' + +Arguments: + - initial (str): optional, key in :ref:`images ` containing the path + of an image that typically initializes DRAM of the target + - image (str): optional, key in :ref:`images ` containing the path + of a bootloader image to load to start of DRAM, or to SRAM when an initial image is unused, or + the path to a vendor loader image typically created using the vendor boot_merger tool + UUUDriver ~~~~~~~~~ A :any:`UUUDriver` is used to upload an image into a device in the *NXP USB diff --git a/labgrid/driver/__init__.py b/labgrid/driver/__init__.py index edf1ad2b1..f1331905b 100644 --- a/labgrid/driver/__init__.py +++ b/labgrid/driver/__init__.py @@ -16,7 +16,8 @@ DigitalOutputPowerDriver, YKUSHPowerDriver, \ USBPowerDriver, SiSPMPowerDriver, NetworkPowerDriver, \ PDUDaemonDriver -from .usbloader import MXSUSBDriver, IMXUSBDriver, BDIMXUSBDriver, RKUSBDriver, UUUDriver +from .usbloader import MXSUSBDriver, IMXUSBDriver, BDIMXUSBDriver, RKUSBDriver, \ + RKUSBMaskromDriver, UUUDriver from .usbsdmuxdriver import USBSDMuxDriver from .usbsdwiredriver import USBSDWireDriver from .common import Driver diff --git a/labgrid/driver/usbloader.py b/labgrid/driver/usbloader.py index 94d24cb95..582ee0148 100644 --- a/labgrid/driver/usbloader.py +++ b/labgrid/driver/usbloader.py @@ -3,8 +3,10 @@ from ..factory import target_factory from ..protocol import BootstrapProtocol +from ..resource.remote import NetworkRKUSBLoader from ..step import step from .common import Driver +from ..util.agentwrapper import AgentWrapper from ..util.managedfile import ManagedFile from ..util.timeout import Timeout from ..util.helper import processwrapper @@ -149,6 +151,53 @@ def load(self, filename=None): raise +@target_factory.reg_driver +@attr.s(eq=False) +class RKUSBMaskromDriver(Driver, BootstrapProtocol): + bindings = {"loader": {"RKUSBLoader", NetworkRKUSBLoader}} + initial = attr.ib(default=None) + image = attr.ib(default=None) + delay = attr.ib(default=0.0, validator=attr.validators.instance_of(float)) + + def __attrs_post_init__(self): + super().__attrs_post_init__() + self.wrapper = None + + def on_activate(self): + if isinstance(self.loader, NetworkRKUSBLoader): + host = self.loader.host + else: + host = None + self.wrapper = AgentWrapper(host) + self.proxy = self.wrapper.load('rkusbmaskrom') + + def on_deactivate(self): + self.proxy = None + if self.wrapper: + self.wrapper.close() + self.wrapper = None + + @Driver.check_active + @step(args=['filename']) + def load(self, filename=None): + images = [] + if self.initial is not None: + images.append(self.target.env.config.get_image_path(self.initial)) + if not filename and self.image is not None: + filename = self.target.env.config.get_image_path(self.image) + if filename: + images.append(filename) + if not images: + raise Exception("No images to load") + + args = [self.loader.busnum, self.loader.devnum] + for path in images: + mf = ManagedFile(path, self.loader) + mf.sync_to_resource() + args.append(mf.get_remote_path()) + self.proxy.load(*args, delay=self.delay) + + @target_factory.reg_driver @attr.s(eq=False) class UUUDriver(Driver, BootstrapProtocol): diff --git a/labgrid/util/agents/rkusbmaskrom.py b/labgrid/util/agents/rkusbmaskrom.py new file mode 100644 index 000000000..af3ca3486 --- /dev/null +++ b/labgrid/util/agents/rkusbmaskrom.py @@ -0,0 +1,233 @@ +""" +This module implements the communication protocol to load an image to SRAM, +that typically initializes DRAM, followed by optionally loading a secondary +image to start of DRAM, when a Rockchip device is in MASKROM mode. +""" +from collections import namedtuple +from struct import unpack +from time import sleep + +import usb.core +import usb.util + + +RK_RC4_KEY = [ + 0x7c, 0x4e, 0x03, 0x04, 0x55, 0x05, 0x09, 0x07, + 0x2d, 0x2c, 0x7b, 0x38, 0x17, 0x0d, 0x17, 0x11, +] + + +# polynomial: 0x1021 +CRC16_TABLE = [ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, +] + + +# polynomial: 0x04c10db7 +CRC32_TABLE = [ + 0x00000000, 0x04c10db7, 0x09821b6e, 0x0d4316d9, 0x130436dc, 0x17c53b6b, 0x1a862db2, 0x1e472005, + 0x26086db8, 0x22c9600f, 0x2f8a76d6, 0x2b4b7b61, 0x350c5b64, 0x31cd56d3, 0x3c8e400a, 0x384f4dbd, + 0x4c10db70, 0x48d1d6c7, 0x4592c01e, 0x4153cda9, 0x5f14edac, 0x5bd5e01b, 0x5696f6c2, 0x5257fb75, + 0x6a18b6c8, 0x6ed9bb7f, 0x639aada6, 0x675ba011, 0x791c8014, 0x7ddd8da3, 0x709e9b7a, 0x745f96cd, + 0x9821b6e0, 0x9ce0bb57, 0x91a3ad8e, 0x9562a039, 0x8b25803c, 0x8fe48d8b, 0x82a79b52, 0x866696e5, + 0xbe29db58, 0xbae8d6ef, 0xb7abc036, 0xb36acd81, 0xad2ded84, 0xa9ece033, 0xa4aff6ea, 0xa06efb5d, + 0xd4316d90, 0xd0f06027, 0xddb376fe, 0xd9727b49, 0xc7355b4c, 0xc3f456fb, 0xceb74022, 0xca764d95, + 0xf2390028, 0xf6f80d9f, 0xfbbb1b46, 0xff7a16f1, 0xe13d36f4, 0xe5fc3b43, 0xe8bf2d9a, 0xec7e202d, + 0x34826077, 0x30436dc0, 0x3d007b19, 0x39c176ae, 0x278656ab, 0x23475b1c, 0x2e044dc5, 0x2ac54072, + 0x128a0dcf, 0x164b0078, 0x1b0816a1, 0x1fc91b16, 0x018e3b13, 0x054f36a4, 0x080c207d, 0x0ccd2dca, + 0x7892bb07, 0x7c53b6b0, 0x7110a069, 0x75d1adde, 0x6b968ddb, 0x6f57806c, 0x621496b5, 0x66d59b02, + 0x5e9ad6bf, 0x5a5bdb08, 0x5718cdd1, 0x53d9c066, 0x4d9ee063, 0x495fedd4, 0x441cfb0d, 0x40ddf6ba, + 0xaca3d697, 0xa862db20, 0xa521cdf9, 0xa1e0c04e, 0xbfa7e04b, 0xbb66edfc, 0xb625fb25, 0xb2e4f692, + 0x8aabbb2f, 0x8e6ab698, 0x8329a041, 0x87e8adf6, 0x99af8df3, 0x9d6e8044, 0x902d969d, 0x94ec9b2a, + 0xe0b30de7, 0xe4720050, 0xe9311689, 0xedf01b3e, 0xf3b73b3b, 0xf776368c, 0xfa352055, 0xfef42de2, + 0xc6bb605f, 0xc27a6de8, 0xcf397b31, 0xcbf87686, 0xd5bf5683, 0xd17e5b34, 0xdc3d4ded, 0xd8fc405a, + 0x6904c0ee, 0x6dc5cd59, 0x6086db80, 0x6447d637, 0x7a00f632, 0x7ec1fb85, 0x7382ed5c, 0x7743e0eb, + 0x4f0cad56, 0x4bcda0e1, 0x468eb638, 0x424fbb8f, 0x5c089b8a, 0x58c9963d, 0x558a80e4, 0x514b8d53, + 0x25141b9e, 0x21d51629, 0x2c9600f0, 0x28570d47, 0x36102d42, 0x32d120f5, 0x3f92362c, 0x3b533b9b, + 0x031c7626, 0x07dd7b91, 0x0a9e6d48, 0x0e5f60ff, 0x101840fa, 0x14d94d4d, 0x199a5b94, 0x1d5b5623, + 0xf125760e, 0xf5e47bb9, 0xf8a76d60, 0xfc6660d7, 0xe22140d2, 0xe6e04d65, 0xeba35bbc, 0xef62560b, + 0xd72d1bb6, 0xd3ec1601, 0xdeaf00d8, 0xda6e0d6f, 0xc4292d6a, 0xc0e820dd, 0xcdab3604, 0xc96a3bb3, + 0xbd35ad7e, 0xb9f4a0c9, 0xb4b7b610, 0xb076bba7, 0xae319ba2, 0xaaf09615, 0xa7b380cc, 0xa3728d7b, + 0x9b3dc0c6, 0x9ffccd71, 0x92bfdba8, 0x967ed61f, 0x8839f61a, 0x8cf8fbad, 0x81bbed74, 0x857ae0c3, + 0x5d86a099, 0x5947ad2e, 0x5404bbf7, 0x50c5b640, 0x4e829645, 0x4a439bf2, 0x47008d2b, 0x43c1809c, + 0x7b8ecd21, 0x7f4fc096, 0x720cd64f, 0x76cddbf8, 0x688afbfd, 0x6c4bf64a, 0x6108e093, 0x65c9ed24, + 0x11967be9, 0x1557765e, 0x18146087, 0x1cd56d30, 0x02924d35, 0x06534082, 0x0b10565b, 0x0fd15bec, + 0x379e1651, 0x335f1be6, 0x3e1c0d3f, 0x3add0088, 0x249a208d, 0x205b2d3a, 0x2d183be3, 0x29d93654, + 0xc5a71679, 0xc1661bce, 0xcc250d17, 0xc8e400a0, 0xd6a320a5, 0xd2622d12, 0xdf213bcb, 0xdbe0367c, + 0xe3af7bc1, 0xe76e7676, 0xea2d60af, 0xeeec6d18, 0xf0ab4d1d, 0xf46a40aa, 0xf9295673, 0xfde85bc4, + 0x89b7cd09, 0x8d76c0be, 0x8035d667, 0x84f4dbd0, 0x9ab3fbd5, 0x9e72f662, 0x9331e0bb, 0x97f0ed0c, + 0xafbfa0b1, 0xab7ead06, 0xa63dbbdf, 0xa2fcb668, 0xbcbb966d, 0xb87a9bda, 0xb5398d03, 0xb1f880b4, +] + + +def crc16_ccitt_false(data, crc=0xffff): + for byte in data: + crc = ((crc << 8) & 0xff00) ^ CRC16_TABLE[((crc >> 8) & 0xff) ^ byte] + return crc & 0xffff + + +def crc32_rkboot(data, crc=0x0): + for byte in data: + crc = ((crc << 8) & 0xffffff00) ^ CRC32_TABLE[((crc >> 24) & 0xff) ^ byte] + return crc & 0xffffffff + + +def rc4_ksa(key): + keylength = len(key) + S = list(range(256)) + j = 0 + for i in range(256): + j = (j + S[i] + key[i % keylength]) % 256 + S[i], S[j] = S[j], S[i] + return S + + +def rc4_prga(S): + i = 0 + j = 0 + while True: + i = (i + 1) % 256 + j = (j + S[i]) % 256 + S[i], S[j] = S[j], S[i] + K = S[(S[i] + S[j]) % 256] + yield K + + +def prase_rkboot_header(data): + RKBootHeader = namedtuple('RKBootHeader', [ + 'tag', 'size', 'version', 'mergerVersion', + 'code471Num', 'code471Offset', 'code471Size', + 'code472Num', 'code472Offset', 'code472Size', + ]) + tag = int.from_bytes(data[:4], 'little') + if tag in (0x544f4f42, 0x2052444c) and \ + crc32_rkboot(data[:-4]) == int.from_bytes(data[-4:], 'little'): + header = RKBootHeader._make(unpack(' 0: + return header + return None + + +def get_rkboot_entries(data, header): + RKBootEntry = namedtuple('RKBootEntry', [ + 'size', 'type', 'dataOffset', 'dataSize', 'dataDelay', + ]) + for code in (0x471, 0x472): + entries = getattr(header, f'code{code:x}Num') + offset = getattr(header, f'code{code:x}Offset') + size = getattr(header, f'code{code:x}Size') + for _ in range(entries): + entry = RKBootEntry._make(unpack('> 8) + data.append(crc & 0xff) + + # extra chunk to signal end of transfer + if len(data) % 4096 == 0: + data.append(0) + + # transfer all chunks + for i in range(0, len(data), 4096): + chunk = data[i:i + 4096] + self._dev.ctrl_transfer(64, 12, 0, code, chunk, 5000) + + +def handle_load(busnum, devnum, initial, secondary=None, delay=None): + with open(initial, 'rb') as f: + data = f.read() + header = prase_rkboot_header(data) + if header is None and secondary is not None: + with open(secondary, 'rb') as f: + data = f.read() + header = prase_rkboot_header(data) + with RKUSBMaskrom(bus=busnum, address=devnum) as maskrom: + if header is not None: + for code, entry_data, entry_delay in get_rkboot_entries(data, header): + maskrom.load(code, entry_data) + if entry_delay: + sleep(entry_delay) + else: + maskrom.load(0x471, initial) + if secondary is not None: + if delay: + sleep(delay) + maskrom.load(0x472, secondary) + + +methods = { + "load": handle_load, +} diff --git a/tests/test_agent.py b/tests/test_agent.py index f5ac21653..278de0082 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -106,6 +106,9 @@ def test_all_modules(): methods = aw.list() assert 'sysfsgpio.set' in methods assert 'sysfsgpio.get' in methods + aw.load('rkusbmaskrom') + methods = aw.list() + assert 'rkusbmaskrom.load' in methods aw.load('usb_hid_relay') methods = aw.list() assert 'usb_hid_relay.set' in methods @@ -114,4 +117,4 @@ def test_all_modules(): def test_import_modules(): import labgrid.util.agents import labgrid.util.agents.dummy - from labgrid.util.agents import deditec_relais8, sysfsgpio + from labgrid.util.agents import deditec_relais8, rkusbmaskrom, sysfsgpio diff --git a/tests/test_rkusbmaskromagent.py b/tests/test_rkusbmaskromagent.py new file mode 100644 index 000000000..30726cee8 --- /dev/null +++ b/tests/test_rkusbmaskromagent.py @@ -0,0 +1,24 @@ +from labgrid.util.agents.rkusbmaskrom import (crc16_ccitt_false, crc32_rkboot, + rc4_ksa, rc4_prga) + + +def test_crc16_ccitt_false(): + crc = crc16_ccitt_false("123456789".encode()) + assert crc == 0x29b1 + + +def test_crc32_rkboot(): + crc = crc32_rkboot("123456789".encode()) + assert crc == 0x889a9615 + + +def test_rc4_keystream(): + # from RFC 6229: Test Vectors for the Stream Cipher RC4 + key = list(bytes.fromhex("1ada31d5cf688221c109163908ebe51debb46227c6cc8b37641910833222772a")) + keystream = rc4_prga(rc4_ksa(key)) + assert [next(keystream) for _ in range(16)] == list(bytes.fromhex("dd5bcb0018e922d494759d7c395d02d3")) + assert [next(keystream) for _ in range(16)] == list(bytes.fromhex("c8446f8f77abf737685353eb89a1c9eb")) + for _ in range(4048): + next(keystream) + assert [next(keystream) for _ in range(16)] == list(bytes.fromhex("d5a39e3dfcc50280bac4a6b5aa0dca7d")) + assert [next(keystream) for _ in range(16)] == list(bytes.fromhex("370b1c1fe655916d97fd0d47ca1d72b8"))