././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1578341034.993396 mboot-0.3.0/0000777000175000000000000000000000000000000013121 5ustar00martinroot00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1578341034.9931386 mboot-0.3.0/PKG-INFO0000777000175000000000000004106400000000000014226 0ustar00martinroot00000000000000Metadata-Version: 2.1 Name: mboot Version: 0.3.0 Summary: Python module for communication with NXP MCU Bootloader Home-page: https://github.com/molejar/pyMBoot Author: Martin Olejar Author-email: martin.olejar@gmail.com License: BSD3 Description: pyMBoot ======= [![Build Status](https://travis-ci.org/molejar/pyMBoot.svg?branch=master)](https://travis-ci.org/molejar/pyMBoot) [![PyPI Status](https://img.shields.io/pypi/v/mboot.svg)](https://pypi.python.org/pypi/mboot) [![Python Version](https://img.shields.io/pypi/pyversions/mboot.svg)](https://www.python.org) pyMBoot is an Open Source python based library for configuring and upgrading the firmware in NXP Microcontrolers via embedded [MCUBOOT](https://www.nxp.com/support/developer-resources/software-development-tools/mcuxpresso-software-and-tools/mcuboot-mcu-bootloader-for-nxp-microcontrollers:MCUBOOT) (MCU Bootloader). Detailed description of `MCUBOOT / KBOOT` key features and functionality is located [here](https://freescale.jiveon.com/docs/DOC-104512). > This project is still in developing phase. Please, test it and report founded issues. Dependencies ------------ - [Python >3.6](https://www.python.org) - The interpreter for Python programing language - [Click](http://click.pocoo.org) - Python package for creating beautiful command line interface. - [bincopy](https://github.com/eerimoq/bincopy) - Python package for parsing S-Record, Intel HEX and TI-TXT files. - [easy_enum](https://github.com/molejar/pyEnum) - User friendly implementation of documented Enum type for Python language. - [PyUSB](https://walac.github.io/pyusb/) - Python package to access USB devices in Linux OS. - [PyWinUSB](https://github.com/rene-aguirre/pywinusb) - Python package that simplifies USB-HID communications on Windows OS. - [pyserial](https://github.com/pyserial/pyserial) - Python package for communication over Serial port in Linux and Windows OS. Installation ------------ ```bash $ pip install mboot ``` To install the latest version from master branch execute in shell following command: ```bash $ pip install -U https://github.com/molejar/pyMBoot/archive/master.zip ``` In case of development, install it from cloned sources: ```bash $ git clone https://github.com/molejar/pyMBoot.git $ cd pyMBoot $ pip install -U -e . ``` **NOTE:** You may run into a permissions issues running these commands. Here are a few options how to fix it: 1. Run with `sudo` to install pyMBoot and dependencies globally 2. Specify the `--user` option to install locally into your home directory (export "~/.local/bin" into PATH variable if haven't). 3. Run the command in a [virtualenv](https://virtualenv.pypa.io/en/latest/) local to a specific project working set. > For running `mboot` module or CLI without root privileges in Linux OS copy following udev rules [90-imx-sdp.rules](https://github.com/molejar/pyIMX/blob/master/udev/90-imx-sdp.rules) into `/etc/udev/rules.d` directory and reload it with command: `sudo udevadm control --reload-rules`. Usage ----- The API of `mboot` module is intuitive and fully reflecting the functionality described in reference manual of any supported device. It's basic usage is presented in following example. ```python import mboot devices = mboot.scan_usb() if devices: mb = mboot.McuBoot(devices[0]) mb.open() # read 100 bytes from address 0 data = mb.read_memory(0, 100) if data is None: print(mb.status_info) mb.close() exit() # other commands ... mb.close() ``` `McuBoot` class is supporting `with` statement what is eliminating the explicit call of `open` and `close` methods. The code then looks more cleaner as you can see in following example. ```python from mboot import scan_usb, McuBoot devices = scan_usb() if devices: with McuBoot(devices[0]) as mb: # read 100 bytes from address 0 data = mb.read_memory(0, 100) if data is None: print(mb.status_info) exit() # other commands ... ``` > If you call `reset()` command inside `with` block, the device is automatically reopened. You can skip this with explicit argument `reset(reopen=False)` By default is command error propagated by return value and must be processed individually for every command. In many use-cases is code execution interrupted if any command finish with error. Therefore you have the option to enable the exception also for command error. The code is then much more readable as you can see in flowing example. ```python from mboot import scan_usb, McuBoot, McuBootError devices = scan_usb() if devices: try: with McuBoot(devices[0], True) as mb: # read 100 bytes from address 0 data = mb.read_memory(0, 100) # other commands ... except McuBootError as e: print(str(e)) ``` `mboot` module is implementing also logging functionality for easy debugging all communication interfaces. To get it working you need only import `logging` module and set the logging level (`DEBUG` or `INFO`) with following line of code: `logging.basicConfig(level=logging.DEBUG)` ```python import logging logging.basicConfig(level=logging.DEBUG) ``` **The example of terminal output with enabled logging functionality:** ```text INFO:MBOOT:Connect: USB COMPOSITE DEVICE (0x15A2, 0x0073) DEBUG:MBOOT:USB:Open Interface INFO:MBOOT:CMD: ReadMemory(address=0x00000000, length=100, mem_id=0) DEBUG:MBOOT:TX-PACKET: Tag=ReadMemory, Flags=0x00, p0=0x00000000, p1=0x00000064, p2=0x00000000 DEBUG:MBOOT:USB:OUT[64]: 01 00 20 00 03 00 00 03 00 00 00 00 64 00 00 00 00 00 00 00 00 00 00 00 00 ... DEBUG:MBOOT:USB:IN [36]: 03 00 0C 00 A3 01 00 02 00 00 00 00 64 00 00 00 00 00 00 00 00 00 00 00 00 ... INFO:MBOOT:RX-PACKET: Tag=ReadMemoryResponse, Status=Success, Length=100 DEBUG:MBOOT:USB:IN [36]: 04 00 20 00 00 60 00 20 C1 00 00 00 0D 85 00 00 09 01 00 00 00 00 00 00 00 ... DEBUG:MBOOT:USB:IN [36]: 04 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 09 01 00 00 00 00 00 00 00 ... DEBUG:MBOOT:USB:IN [36]: 04 00 20 00 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 09 ... DEBUG:MBOOT:USB:IN [36]: 04 00 04 00 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 09 ... DEBUG:MBOOT:USB:IN [36]: 03 00 0C 00 A0 00 00 02 00 00 00 00 03 00 00 00 09 01 00 00 09 01 00 00 09 ... DEBUG:MBOOT:RX-PACKET: Tag=GenericResponse, Status=Success, Cmd=ReadMemory INFO:MBOOT:CMD: Successfully Received 100 from 100 Bytes DEBUG:MBOOT:USB:Close Interface ``` [ mboot ] Tool -------------- The `mboot` module is distributed with command-line utility, which demonstrate the complete functionality of this library and can be used as replacement of `blhos` tool. If you write `mboot` into shell and click enter, then you get the description of its usage. For getting the help of individual commands just use `mboot -?`. ``` bash $ mboot --help Usage: mboot [OPTIONS] COMMAND [ARGS]... NXP MCU Bootloader Command Line Interface, version: 0.3.0 NOTE: Development version, be carefully with it usage ! Options: -t, --target TEXT Select target MKL27, LPC55, ... [optional] -d, --debug INTEGER RANGE Debug level: 0-off, 1-info, 2-debug -v, --version Show the version and exit. -?, --help Show this message and exit. Commands: call Call code from specified address efuse Read/Write eFuse from MCU erase Erase MCU internal or external memory execute Execute code from specified address fill Fill MCU memory with specified pattern info Get MCU info (mboot properties) keyblob Generate the Blob for given DEK Key kp-enroll Key provisioning: Enroll kp-gen-key Key provisioning: Generate Intrinsic Key kp-read-kstore Key provisioning: Read the key from key store area kp-read-nvm Key provisioning: Read the key from nonvolatile memory kp-user-key Key provisioning: Send the user key to a bootloader kp-write-kstore Key provisioning: Write the key into key store area kp-write-nvm Key provisioning: Write the key into nonvolatile memory mconf Configure external memory mlist Get list of available memories otp Read/Write internal OTP segment read Read data from MCU internal or external memory reset Reset MCU resource Flash read resource sbfile Receive SB file unlock Unlock MCU update Copy backup app from address to main app region write Write data into MCU internal or external memory ``` > If USB device is not in known devices list, then use `-t or --target` argument and directly specify the device VID:PID. Example: **-t 0x15A2:0x0073**
#### $ mboot info Read bootloader properties from connected MCU. ```bash $ mboot info DEVICE: Kinetis Bootloader (0x15A2, 0x0073) CurrentVersion: K1.0.0 AvailablePeripherals: - UART - I2C-Slave - SPI-Slave - USB-HID FlashStartAddress: 0x00000000 FlashSize: 256.0 kiB FlashSectorSize: 1.0 kiB FlashBlockCount: 2 AvailableCommands: - FlashEraseAll - FlashEraseRegion - ReadMemory - FillMemory - FlashSecurityDisable - ReceiveSBFile - Call - Reset - SetProperty VerifyWrites: ON MaxPacketSize: 32 B ReservedRegions: - 0x1FFFF800 - 0x20000687, 3.6 kiB ValidateRegions: ON RamStartAddress: 0x1FFFE000 RamSize: 32.0 kiB SystemDeviceIdent: 0x23160D82 FlashSecurityState: Unlocked ```
#### $ mboot mlist Get list of available memories (internal and external) ```bash $ mboot info DEVICE: Kinetis Bootloader (0x15A2, 0x0073) Internal Flash: 0) 0x00000000 - 0x00040000, Size: 256.0 kiB, Sector Size: 1.0 kiB Internal Ram: 0) 0x1FFFE000 - 0x20006000, Size: 32.0 kiB ```
#### $ mboot read [OPTIONS] ADDRESS [LENGTH] Read data from MCU memory and store it into file as binary (*.bin), intel-hex (*.hex, *.ihex) or s-record (*.srec, *.s19) format. If output file is not specified, the data are dumped into stdout in readable format. > LENGTH argument is optional and as default will be used the size to end of memory ##### options: * **-c, --compress** - Compress dump output. (default: False) * **-f, --file** - Output file name with extension: *.bin, *.hex, *.ihex, *.srec or *.s19 * **-?, --help** - Show help message and exit. ``` bash $ mboot read 0 200 Reading from MCU memory, please wait ! ADDRESS | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F | 0123456789ABCDEF ----------------------------------------------------------------------------- 00000000 | 00 60 00 20 C1 00 00 00 D9 08 00 00 09 01 00 00 | .`. ............ 00000010 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00000020 | 00 00 00 00 00 00 00 00 00 00 00 00 09 01 00 00 | ................ 00000030 | 00 00 00 00 00 00 00 00 09 01 00 00 09 01 00 00 | ................ 00000040 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000050 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000060 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000070 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000080 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000090 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 000000A0 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 000000B0 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 000000C0 | 0A 49 0B 4A 0B 4B 9B 1A | .I.J.K.. ----------------------------------------------------------------------------- ```
#### $ mboot write [OPTIONS] FILE Write data from attached FILE into MCU memory. ##### options: * **-a, --address** - Start Address. (default: 0) * **-o, --offset** - Offset of input data. (default: 0) * **-?, --help** - Show help message and exit. ``` bash $ mboot write blink.srec Wrote Successfully. ```
#### $ mboot erase [OPTIONS] Erase MCU memory from specified address and length or complete chip. ##### options: * **-m, --mass** - Erase complete MCU memory. * **-a, --address** - Start Address. * **-l, --length** - Count of bytes aligned to flash block size. * **-?, --help** - Show help message and exit. ``` bash $ mboot erase -m Chip Erased Successfully. ```
#### $ mboot unlock [OPTIONS] Unlock MCU memory. ##### options: * **-k, --key** - Use backdoor key as ASCII = S:123...8 or HEX = X:010203...08 * **-?, --help** - Show help message and exit. ``` bash $ mboot unlock Chip Unlocked Successfully. ```
#### $ mboot fill [OPTIONS] ADDRESS LENGTH Fill MCU memory with specified pattern ##### options: * **-p, --pattern** - Pattern format (default: 0xFFFFFFFF). * **-?, --help** - Show help message and exit. ``` bash $ mboot fill -p 0x11111111 0x1FFFE000 10 Filled Successfully. ```
#### $ mboot reset MCU SW reset ``` bash $ mboot reset ``` TODO ---- - Implement support for UART interface Platform: UNKNOWN Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX :: Linux Classifier: Environment :: Console Classifier: Topic :: Scientific/Engineering Classifier: Topic :: Software Development :: Embedded Systems Classifier: Topic :: Utilities Requires-Python: >=3.6 Description-Content-Type: text/markdown ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578339313.0 mboot-0.3.0/README.md0000777000175000000000000003136200000000000014410 0ustar00martinroot00000000000000pyMBoot ======= [![Build Status](https://travis-ci.org/molejar/pyMBoot.svg?branch=master)](https://travis-ci.org/molejar/pyMBoot) [![PyPI Status](https://img.shields.io/pypi/v/mboot.svg)](https://pypi.python.org/pypi/mboot) [![Python Version](https://img.shields.io/pypi/pyversions/mboot.svg)](https://www.python.org) pyMBoot is an Open Source python based library for configuring and upgrading the firmware in NXP Microcontrolers via embedded [MCUBOOT](https://www.nxp.com/support/developer-resources/software-development-tools/mcuxpresso-software-and-tools/mcuboot-mcu-bootloader-for-nxp-microcontrollers:MCUBOOT) (MCU Bootloader). Detailed description of `MCUBOOT / KBOOT` key features and functionality is located [here](https://freescale.jiveon.com/docs/DOC-104512). > This project is still in developing phase. Please, test it and report founded issues. Dependencies ------------ - [Python >3.6](https://www.python.org) - The interpreter for Python programing language - [Click](http://click.pocoo.org) - Python package for creating beautiful command line interface. - [bincopy](https://github.com/eerimoq/bincopy) - Python package for parsing S-Record, Intel HEX and TI-TXT files. - [easy_enum](https://github.com/molejar/pyEnum) - User friendly implementation of documented Enum type for Python language. - [PyUSB](https://walac.github.io/pyusb/) - Python package to access USB devices in Linux OS. - [PyWinUSB](https://github.com/rene-aguirre/pywinusb) - Python package that simplifies USB-HID communications on Windows OS. - [pyserial](https://github.com/pyserial/pyserial) - Python package for communication over Serial port in Linux and Windows OS. Installation ------------ ```bash $ pip install mboot ``` To install the latest version from master branch execute in shell following command: ```bash $ pip install -U https://github.com/molejar/pyMBoot/archive/master.zip ``` In case of development, install it from cloned sources: ```bash $ git clone https://github.com/molejar/pyMBoot.git $ cd pyMBoot $ pip install -U -e . ``` **NOTE:** You may run into a permissions issues running these commands. Here are a few options how to fix it: 1. Run with `sudo` to install pyMBoot and dependencies globally 2. Specify the `--user` option to install locally into your home directory (export "~/.local/bin" into PATH variable if haven't). 3. Run the command in a [virtualenv](https://virtualenv.pypa.io/en/latest/) local to a specific project working set. > For running `mboot` module or CLI without root privileges in Linux OS copy following udev rules [90-imx-sdp.rules](https://github.com/molejar/pyIMX/blob/master/udev/90-imx-sdp.rules) into `/etc/udev/rules.d` directory and reload it with command: `sudo udevadm control --reload-rules`. Usage ----- The API of `mboot` module is intuitive and fully reflecting the functionality described in reference manual of any supported device. It's basic usage is presented in following example. ```python import mboot devices = mboot.scan_usb() if devices: mb = mboot.McuBoot(devices[0]) mb.open() # read 100 bytes from address 0 data = mb.read_memory(0, 100) if data is None: print(mb.status_info) mb.close() exit() # other commands ... mb.close() ``` `McuBoot` class is supporting `with` statement what is eliminating the explicit call of `open` and `close` methods. The code then looks more cleaner as you can see in following example. ```python from mboot import scan_usb, McuBoot devices = scan_usb() if devices: with McuBoot(devices[0]) as mb: # read 100 bytes from address 0 data = mb.read_memory(0, 100) if data is None: print(mb.status_info) exit() # other commands ... ``` > If you call `reset()` command inside `with` block, the device is automatically reopened. You can skip this with explicit argument `reset(reopen=False)` By default is command error propagated by return value and must be processed individually for every command. In many use-cases is code execution interrupted if any command finish with error. Therefore you have the option to enable the exception also for command error. The code is then much more readable as you can see in flowing example. ```python from mboot import scan_usb, McuBoot, McuBootError devices = scan_usb() if devices: try: with McuBoot(devices[0], True) as mb: # read 100 bytes from address 0 data = mb.read_memory(0, 100) # other commands ... except McuBootError as e: print(str(e)) ``` `mboot` module is implementing also logging functionality for easy debugging all communication interfaces. To get it working you need only import `logging` module and set the logging level (`DEBUG` or `INFO`) with following line of code: `logging.basicConfig(level=logging.DEBUG)` ```python import logging logging.basicConfig(level=logging.DEBUG) ``` **The example of terminal output with enabled logging functionality:** ```text INFO:MBOOT:Connect: USB COMPOSITE DEVICE (0x15A2, 0x0073) DEBUG:MBOOT:USB:Open Interface INFO:MBOOT:CMD: ReadMemory(address=0x00000000, length=100, mem_id=0) DEBUG:MBOOT:TX-PACKET: Tag=ReadMemory, Flags=0x00, p0=0x00000000, p1=0x00000064, p2=0x00000000 DEBUG:MBOOT:USB:OUT[64]: 01 00 20 00 03 00 00 03 00 00 00 00 64 00 00 00 00 00 00 00 00 00 00 00 00 ... DEBUG:MBOOT:USB:IN [36]: 03 00 0C 00 A3 01 00 02 00 00 00 00 64 00 00 00 00 00 00 00 00 00 00 00 00 ... INFO:MBOOT:RX-PACKET: Tag=ReadMemoryResponse, Status=Success, Length=100 DEBUG:MBOOT:USB:IN [36]: 04 00 20 00 00 60 00 20 C1 00 00 00 0D 85 00 00 09 01 00 00 00 00 00 00 00 ... DEBUG:MBOOT:USB:IN [36]: 04 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 09 01 00 00 00 00 00 00 00 ... DEBUG:MBOOT:USB:IN [36]: 04 00 20 00 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 09 ... DEBUG:MBOOT:USB:IN [36]: 04 00 04 00 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 09 ... DEBUG:MBOOT:USB:IN [36]: 03 00 0C 00 A0 00 00 02 00 00 00 00 03 00 00 00 09 01 00 00 09 01 00 00 09 ... DEBUG:MBOOT:RX-PACKET: Tag=GenericResponse, Status=Success, Cmd=ReadMemory INFO:MBOOT:CMD: Successfully Received 100 from 100 Bytes DEBUG:MBOOT:USB:Close Interface ``` [ mboot ] Tool -------------- The `mboot` module is distributed with command-line utility, which demonstrate the complete functionality of this library and can be used as replacement of `blhos` tool. If you write `mboot` into shell and click enter, then you get the description of its usage. For getting the help of individual commands just use `mboot -?`. ``` bash $ mboot --help Usage: mboot [OPTIONS] COMMAND [ARGS]... NXP MCU Bootloader Command Line Interface, version: 0.3.0 NOTE: Development version, be carefully with it usage ! Options: -t, --target TEXT Select target MKL27, LPC55, ... [optional] -d, --debug INTEGER RANGE Debug level: 0-off, 1-info, 2-debug -v, --version Show the version and exit. -?, --help Show this message and exit. Commands: call Call code from specified address efuse Read/Write eFuse from MCU erase Erase MCU internal or external memory execute Execute code from specified address fill Fill MCU memory with specified pattern info Get MCU info (mboot properties) keyblob Generate the Blob for given DEK Key kp-enroll Key provisioning: Enroll kp-gen-key Key provisioning: Generate Intrinsic Key kp-read-kstore Key provisioning: Read the key from key store area kp-read-nvm Key provisioning: Read the key from nonvolatile memory kp-user-key Key provisioning: Send the user key to a bootloader kp-write-kstore Key provisioning: Write the key into key store area kp-write-nvm Key provisioning: Write the key into nonvolatile memory mconf Configure external memory mlist Get list of available memories otp Read/Write internal OTP segment read Read data from MCU internal or external memory reset Reset MCU resource Flash read resource sbfile Receive SB file unlock Unlock MCU update Copy backup app from address to main app region write Write data into MCU internal or external memory ``` > If USB device is not in known devices list, then use `-t or --target` argument and directly specify the device VID:PID. Example: **-t 0x15A2:0x0073**
#### $ mboot info Read bootloader properties from connected MCU. ```bash $ mboot info DEVICE: Kinetis Bootloader (0x15A2, 0x0073) CurrentVersion: K1.0.0 AvailablePeripherals: - UART - I2C-Slave - SPI-Slave - USB-HID FlashStartAddress: 0x00000000 FlashSize: 256.0 kiB FlashSectorSize: 1.0 kiB FlashBlockCount: 2 AvailableCommands: - FlashEraseAll - FlashEraseRegion - ReadMemory - FillMemory - FlashSecurityDisable - ReceiveSBFile - Call - Reset - SetProperty VerifyWrites: ON MaxPacketSize: 32 B ReservedRegions: - 0x1FFFF800 - 0x20000687, 3.6 kiB ValidateRegions: ON RamStartAddress: 0x1FFFE000 RamSize: 32.0 kiB SystemDeviceIdent: 0x23160D82 FlashSecurityState: Unlocked ```
#### $ mboot mlist Get list of available memories (internal and external) ```bash $ mboot info DEVICE: Kinetis Bootloader (0x15A2, 0x0073) Internal Flash: 0) 0x00000000 - 0x00040000, Size: 256.0 kiB, Sector Size: 1.0 kiB Internal Ram: 0) 0x1FFFE000 - 0x20006000, Size: 32.0 kiB ```
#### $ mboot read [OPTIONS] ADDRESS [LENGTH] Read data from MCU memory and store it into file as binary (*.bin), intel-hex (*.hex, *.ihex) or s-record (*.srec, *.s19) format. If output file is not specified, the data are dumped into stdout in readable format. > LENGTH argument is optional and as default will be used the size to end of memory ##### options: * **-c, --compress** - Compress dump output. (default: False) * **-f, --file** - Output file name with extension: *.bin, *.hex, *.ihex, *.srec or *.s19 * **-?, --help** - Show help message and exit. ``` bash $ mboot read 0 200 Reading from MCU memory, please wait ! ADDRESS | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F | 0123456789ABCDEF ----------------------------------------------------------------------------- 00000000 | 00 60 00 20 C1 00 00 00 D9 08 00 00 09 01 00 00 | .`. ............ 00000010 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00000020 | 00 00 00 00 00 00 00 00 00 00 00 00 09 01 00 00 | ................ 00000030 | 00 00 00 00 00 00 00 00 09 01 00 00 09 01 00 00 | ................ 00000040 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000050 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000060 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000070 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000080 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000090 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 000000A0 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 000000B0 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 000000C0 | 0A 49 0B 4A 0B 4B 9B 1A | .I.J.K.. ----------------------------------------------------------------------------- ```
#### $ mboot write [OPTIONS] FILE Write data from attached FILE into MCU memory. ##### options: * **-a, --address** - Start Address. (default: 0) * **-o, --offset** - Offset of input data. (default: 0) * **-?, --help** - Show help message and exit. ``` bash $ mboot write blink.srec Wrote Successfully. ```
#### $ mboot erase [OPTIONS] Erase MCU memory from specified address and length or complete chip. ##### options: * **-m, --mass** - Erase complete MCU memory. * **-a, --address** - Start Address. * **-l, --length** - Count of bytes aligned to flash block size. * **-?, --help** - Show help message and exit. ``` bash $ mboot erase -m Chip Erased Successfully. ```
#### $ mboot unlock [OPTIONS] Unlock MCU memory. ##### options: * **-k, --key** - Use backdoor key as ASCII = S:123...8 or HEX = X:010203...08 * **-?, --help** - Show help message and exit. ``` bash $ mboot unlock Chip Unlocked Successfully. ```
#### $ mboot fill [OPTIONS] ADDRESS LENGTH Fill MCU memory with specified pattern ##### options: * **-p, --pattern** - Pattern format (default: 0xFFFFFFFF). * **-?, --help** - Show help message and exit. ``` bash $ mboot fill -p 0x11111111 0x1FFFE000 10 Filled Successfully. ```
#### $ mboot reset MCU SW reset ``` bash $ mboot reset ``` TODO ---- - Implement support for UART interface ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1578341034.9876132 mboot-0.3.0/mboot/0000777000175000000000000000000000000000000014241 5ustar00martinroot00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578247081.0 mboot-0.3.0/mboot/__init__.py0000777000175000000000000000203000000000000016350 0ustar00martinroot00000000000000# Copyright (c) 2017 Martin Olejar # # SPDX-License-Identifier: BSD-3-Clause # The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution # or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText from .mcuboot import McuBoot from .commands import CommandTag from .memories import ExtMemPropTags, ExtMemId from .properties import PropertyTag, PeripheryTag, Version, parse_property_value from .exceptions import McuBootError, McuBootCommandError, McuBootConnectionError from .errorcodes import StatusCode from .connection import scan_usb __author__ = "Martin Olejar" __contact__ = "martin.olejar@gmail.com" __version__ = '0.3.0' __license__ = "BSD3" __status__ = 'Development' __all__ = [ # global methods 'scan_usb', 'parse_property_value', # classes 'McuBoot', 'Version', # enums 'PropertyTag', 'PeripheryTag', 'CommandTag', 'StatusCode', 'ExtMemId', # exceptions 'McuBootError', 'McuBootCommandError', 'McuBootConnectionError' ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578307626.0 mboot-0.3.0/mboot/__main__.py0000777000175000000000000007463400000000000016354 0ustar00martinroot00000000000000# Copyright (c) 2017 Martin Olejar # # SPDX-License-Identifier: BSD-3-Clause # The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution # or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText import os import sys import click import bincopy import traceback from mboot import McuBoot, scan_usb, ExtMemId, CommandTag, PropertyTag, parse_property_value ######################################################################################################################## # Helper methods ######################################################################################################################## def hexdump(data, start_address=0, compress=True, length=16, sep='.'): """ Return string array in hex-dump format :param data: The data array of bytes :param start_address: Absolute Start Address :param compress: Compressed output (remove duplicated content, rows) :param length: Number of Bytes for row (max 16) :param sep: Is used for non ASCII char """ msg = [] # The max line length is 16 bytes if length > 16: length = 16 # Create header header = ' ADDRESS | ' for i in range(0, length): header += "{:02X} ".format(i) header += '| ' for i in range(0, length): header += "{:X}".format(i) msg.append(header) msg.append((' ' + '-' * (13 + 4 * length))) # Check address align offset = start_address % length address = start_address - offset align = True if (offset > 0) else False # Print flags prev_line = None print_mark = True # process data for i in range(0, len(data) + offset, length): hexa = '' if align: substr = data[0: length - offset] else: substr = data[i - offset: i + length - offset] if compress: # compress output string if substr == prev_line: if print_mark: print_mark = False msg.append(' *') continue else: prev_line = substr print_mark = True if align: hexa += ' ' * offset for h in range(0, len(substr)): h = substr[h] if not isinstance(h, int): h = ord(h) hexa += "{:02X} ".format(h) text = '' if align: text += ' ' * offset for c in substr: if not isinstance(c, int): c = ord(c) if 0x20 <= c < 0x7F: text += chr(c) else: text += sep msg.append((' {:08X} | {:<' + str(length * 3) + 's}| {:s}').format(address + i, hexa, text)) align = False msg.append((' ' + '-' * (13 + 4 * length))) return '\n'.join(msg) def size_fmt(num, kibibyte=True): base, suffix = [(1000., 'B'), (1024., 'iB')][kibibyte] for x in ['B'] + [x + suffix for x in list('kMGTP')]: if -base < num < base: break num /= base return "{} {}".format(num, x) if x == 'B' else "{:3.1f} {}".format(num, x) class UInt(click.ParamType): """ Custom argument type for unsigned integer """ name = 'uint' def __init__(self, min=None, max=None, clamp=False): self.min = min self.max = max self.clamp = clamp def __repr__(self): return 'UINT' def convert(self, value, param, ctx): try: if not isinstance(value, int): value = int(value, 0) except: self.fail('{} is not a valid value format.'.format(value), param, ctx) if self.clamp: if self.min is not None and value < self.min: return self.min if self.max is not None and value > self.max: return self.max if self.min is not None and value < self.min or self.max is not None and value > self.max: if self.min is None: self.fail('{} is bigger than the maximum valid value {}.'.format(value, self.max), param, ctx) elif self.max is None: self.fail('{} is smaller than the minimum valid value {}.'.format(value, self.min), param, ctx) else: self.fail('{} is not in the valid range of {} to {}.'.format(value, self.min, self.max), param, ctx) return value class BDKey(click.ParamType): """ Custom argument type for BackDoor Key """ name = 'backdoor key' def __repr__(self): return 'BDKEY' def convert(self, value, param, ctx): if value[0] == 'S': if len(value) < 18: self.fail('Short key, use 16 ASCII chars !', param, ctx) backdoor_key = [ord(k) for k in value[2:]] else: if len(value) < 34: self.fail('Short key, use 32 HEX chars !', param, ctx) value = value[2:] backdoor_key = [] try: for i in range(0, len(value), 2): backdoor_key.append(int(value[i:i+2], 16)) except ValueError: self.fail('Unsupported HEX char in Key !', param, ctx) return backdoor_key class ImgFile(click.ParamType): """ Custom argument type for Image File """ name = 'file' def __init__(self, *extensions, exists=False): self.exists = exists self.valid_extensions = extensions def __repr__(self): return 'FILE' def convert(self, value, param, ctx): if not value.lower().endswith(self.valid_extensions): self.fail('Unsupported file type: *.{} !'.format(value.split('.')[-1]), param, ctx) if self.exists and not os.path.lexists(value): self.fail('File "{}" does not exist !'.format(value), param, ctx) return value ######################################################################################################################## # McuBoot CLI ######################################################################################################################## # Application error code ERROR_CODE = 1 # Application version VERSION = '0.3' # Application description DESCRIP = ( "NXP MCU Bootloader Command Line Interface, version: " + VERSION + " \n\n" "NOTE: Development version, be carefully with it usage !\n" ) # List of supported memories MEMS = ['INTERNAL'] + [name for name, _, _ in ExtMemId] # helper method def print_error(message, debug=False): click.echo('\n' + traceback.format_exc() if debug else ' ' + message) sys.exit(ERROR_CODE) # helper method def scan_interface(device_name): # Scan for connected devices devs = scan_usb(device_name) if devs: index = 0 if len(devs) > 1: click.echo('') for i, dev in enumerate(devs): click.secho("{}) {}".format(i, dev.info())) click.echo('\n Select: ', nl=False) c = input() click.echo() index = int(c, 10) click.secho(" DEVICE: {}\n".format(devs[index].info())) return devs[index] else: print_error("Device not connected !\n") # McuBoot: base options @click.group(context_settings=dict(help_option_names=['-?', '--help']), help=DESCRIP) @click.option('-t', '--target', type=click.STRING, default=None, help='Select target MKL27, LPC55, ... [optional]') @click.option('-d', "--debug", type=click.IntRange(0, 2, True), default=0, help='Debug level: 0-off, 1-info, 2-debug') @click.version_option(VERSION, '-v', '--version') @click.pass_context def cli(ctx, target, debug): if debug > 0: import logging log_level = [logging.NOTSET, logging.INFO, logging.DEBUG] logging.basicConfig(level=log_level[debug]) ctx.obj['DEBUG'] = debug ctx.obj['TARGET'] = target click.echo() # McuBoot: MCU info command @cli.command(short_help="Get MCU info (mboot properties)") @click.pass_context def info(ctx): properties = [] device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: properties = mb.get_property_list() except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() for p in properties: v = p.to_str() if isinstance(v, list): click.echo(f" {p.name}:" + "".join([f"\n - {s}" for s in v])) else: click.echo(f" {p.name}: {v}") # McuBoot: print memories list command @cli.command(short_help="Get list of available memories") @click.pass_context def mlist(ctx): mem_list = {} device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: mem_list = mb.get_memory_list() except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() message = '' for key, values in mem_list.items(): message += " {}:\n".format(key.title().replace('_', ' ')) if key in ('internal_ram', 'internal_flash'): for i, item in values.items(): message += " {}) 0x{:08X} - 0x{:08X}, Size: {}".format( i, item['address'], item['address'] + item['size'], size_fmt(item['size'])) if 'sector_size' in item: message += ", Sector Size: {}".format(size_fmt(item['sector_size'])) message += '\n' else: for i, attr in enumerate(values): message += " {}) {}:\n".format(i, attr['mem_name']) if 'address' in attr: message += " Start Address: 0x{:08X}\n".format(attr['address']) if 'size' in attr: message += " Memory Size: {} ({} B)\n".format(size_fmt(attr['size']), attr['size']) if 'page_size' in attr: message += " Page Size: {}\n".format(attr['page_size']) if 'sector_size' in attr: message += " Sector Size: {}\n".format(attr['sector_size']) if 'block_size' in attr: message += " Block Size: {}\n".format(attr['block_size']) message += '\n' click.echo(message) # McuBoot: configure external memory command @cli.command(short_help="Configure external memory") @click.option('-a', '--address', type=UInt(), default=None, help='Location inside RAM for storing config data') @click.option('-w', '--word', type=UInt(), multiple=True, default=None, help='Configuration word') @click.option('-t', '--mtype', type=click.Choice(MEMS[1:]), default='QSPI', show_default=True, help='Memory Type') @click.option('-f', '--file', type=ImgFile('.conf', exists=True), help='Memory configuration file') @click.pass_context def mconf(ctx, address, word, mtype, file): memory_id = ExtMemId[mtype] memory_data = bytes() if word: for w in word: memory_data += w.to_bytes(4, 'little') if file is not None: print_error("Not implemented yet load memory configuration from file") # load memory configuration fom file with open(file, 'r') as f: # TODO: add file parser into memory_data pass if not memory_data: print_error('The argument -w/--word or -f/--file must be specified !') device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: if address is None: # get internal memory start address and size memory_address = mb.get_property(PropertyTag.RAM_START_ADDRESS)[0] memory_size = mb.get_property(PropertyTag.RAM_SIZE)[0] # calculate address address = memory_address + memory_size - len(memory_data) # add additional offset 1024 Bytes address -= 1024 mb.write_memory(address, memory_data) mb.configure_memory(memory_id, address) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() # McuBoot: receive SB file command @cli.command(short_help="Receive SB file") @click.argument('file', nargs=1, type=ImgFile(('.bin', '.sb', '.sb2'), True)) @click.pass_context def sbfile(ctx, file): device = scan_interface(ctx.obj['TARGET']) with open(file, 'rb') as f: sb_data = f.read() try: with McuBoot(device, True) as mb: mb.receive_sb_file(sb_data) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() # McuBoot: memory write command @cli.command(short_help="Write data into MCU internal or external memory") @click.option('-a', '--address', type=UInt(), default=None, help='Start Address.') @click.option('-o', '--offset', type=UInt(), default=0, show_default=True, help='Offset of input data.') @click.option('-t', '--mtype', type=click.Choice(MEMS), default='INTERNAL', show_default=True, help='Memory Type') @click.option('-e', '--erase', is_flag=True, default=False, help='Erase') @click.option('-v', '--verify', is_flag=True, default=False, help='Verify') @click.argument('file', nargs=1, type=ImgFile('.bin', '.hex', '.ihex', '.s19', '.srec', exists=True)) @click.pass_context def write(ctx, address, offset, mtype, erase, verify, file): mem_id = 0 if mtype == 'INTERNAL' else ExtMemId[mtype] in_data = bincopy.BinFile() try: if file.lower().endswith(('.srec', '.s19')): in_data.add_srec_file(file) if address is None: address = in_data.minimum_address elif file.lower().endswith(('.hex', '.ihex')): in_data.add_ihex_file(file) if address is None: address = in_data.minimum_address else: in_data.add_binary_file(file) if address is None: address = 0 data = in_data.as_binary() except Exception as e: print_error(f"Could not read from file: {file} \n [{str(e)}]") raise if offset < len(data): data = data[offset:] device = scan_interface(ctx.obj['TARGET']) click.echo(' Writing into MCU memory, please wait !\n') try: with McuBoot(device, True) as mb: # Read Flash Sector Size of connected MCU flash_sector_size = mb.get_property(PropertyTag.FLASH_SECTOR_SIZE, mem_id)[0] # Align Erase Start Address and Len to Flash Sector Size start_address = (address & ~(flash_sector_size - 1)) length = (len(data) & ~(flash_sector_size - 1)) if (len(data) % flash_sector_size) > 0: length += flash_sector_size # Erase specified region in MCU Flash memory mb.flash_erase_region(start_address, length, mem_id) # Write data into MCU Flash memory mb.write_memory(address, data, mem_id) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() click.echo(" Wrote Successfully.") # McuBoot: memory read command @cli.command(short_help="Read data from MCU internal or external memory") @click.option('-t', '--mtype', type=click.Choice(MEMS), default='INTERNAL', show_default=True, help='Memory Type') @click.option('-c', '--compress', is_flag=True, show_default=True, help='Compress dump output.') @click.option('-f', '--file', type=ImgFile('.bin', '.hex', '.ihex', '.s19', '.srec'), help='Output file name with ext.: *.bin, *.hex, *.ihex, *.srec or *.s19') @click.argument('address', type=UInt()) @click.argument('length', type=UInt()) @click.pass_context def read(ctx, address, length, mtype, compress, file): mem_id = 0 if mtype == 'INTERNAL' else ExtMemId[mtype] device = scan_interface(ctx.obj['TARGET']) click.echo(" Reading from MCU memory, please wait ! \n") try: with McuBoot(device, True) as mb: data = mb.read_memory(address, length, mem_id) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() if file is None: click.echo(hexdump(data, address, compress)) else: try: if file.lower().endswith(('.srec', '.s19')): srec = bincopy.BinFile() srec.add_binary(data, address) srec.header = 'mboot' with open(file, "w") as f: f.write(srec.as_srec()) elif file.lower().endswith(('.hex', '.ihex')): ihex = bincopy.BinFile() ihex.add_binary(data, address) with open(file, "w") as f: f.write(ihex.as_ihex()) else: with open(file, "wb") as f: f.write(data) except Exception as e: print_error(f"Could not write to file: {file} \n [{str(e)}]") click.echo(f"\n Successfully saved into: {file}") # McuBoot: memory erase command @cli.command(short_help="Erase MCU internal or external memory") @click.option('-m/', '--mass/', is_flag=True, default=False, help='Erase complete memory') @click.option('-a', '--address', type=UInt(), help='Start Address.') @click.option('-l', '--length', type=UInt(), help='Count of bytes aligned to flash block size') @click.option('-t', '--mtype', type=click.Choice(MEMS), default='INTERNAL', show_default=True, help='Memory Type') @click.pass_context def erase(ctx, address, length, mass, mtype): mem_id = 0 if mtype == 'INTERNAL' else ExtMemId[mtype] device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: if mass: values = mb.get_property(PropertyTag.AVAILABLE_COMMANDS) commands = parse_property_value(PropertyTag.AVAILABLE_COMMANDS, values) if CommandTag.FLASH_ERASE_ALL_UNSECURE in commands: mb.flash_erase_all_unsecure() elif CommandTag.FLASH_ERASE_ALL in commands: mb.flash_erase_all(mem_id) else: raise Exception('Not Supported Command') else: if address is None or length is None: raise Exception("Argument \"-a, --address\" and \"-l, --length\" must be defined !") mb.flash_erase_region(address, length, mem_id) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() click.secho(" Erased Successfully.") # McuBoot: eFuse read/write command @cli.command(short_help="Read/Write eFuse from MCU") @click.argument('index', type=UInt()) @click.argument('value', type=UInt(), required=False) @click.pass_context def efuse(ctx, index, value): read_value = 0 device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: if value is not None: mb.efuse_program_once(index, value) read_value = mb.efuse_read_once(index) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() click.echo(f" eFuse[{index}] = 0x{read_value:08X}") # McuBoot: OTP read/write command @cli.command(short_help="Read/Write internal OTP segment") @click.option('-l', '--length', type=UInt(), default=4, show_default=True, help='Bytes count') @click.argument('address', type=UInt()) @click.argument('data', type=UInt(), required=False) @click.pass_context def otp(ctx, length, address, data): print_error("ERROR: 'otp' command is not implemented yet") device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: # TODO: write implementation pass except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() # McuBoot: read resource command @cli.command(short_help="Flash read resource") @click.option('-o', '--option', type=UInt(), default=0, show_default=True, help='Option') @click.option('-c', '--compress', is_flag=True, show_default=True, help='Compress dump output.') @click.option('-f', '--file', type=ImgFile('.bin', '.hex', '.ihex', '.s19', '.srec'), help='Output file name with ext.: *.bin, *.hex, *.ihex, *.srec or *.s19') @click.argument('address', type=UInt()) @click.argument('length', type=UInt()) @click.pass_context def resource(ctx, address, length, option, compress, file): device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: data = mb.flash_read_resource(address, length, option) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() if file is None: click.echo(hexdump(data, address, compress)) else: try: if file.lower().endswith(('.srec', '.s19')): srec = bincopy.BinFile() srec.add_binary(data, address) srec.header = 'mboot' with open(file, "w") as f: f.write(srec.as_srec()) elif file.lower().endswith(('.hex', '.ihex')): ihex = bincopy.BinFile() ihex.add_binary(data, address) with open(file, "w") as f: f.write(ihex.as_ihex()) else: with open(file, "wb") as f: f.write(data) except Exception as e: print_error(f'Could not write to file: {file} \n [{str(e)}]') click.echo(f" Successfully saved into: {file}") # McuBoot: unlock command @cli.command(short_help="Unlock MCU") @click.option('-k', '--key', type=BDKey(), help='Use backdoor key as ASCI = S:123...8 or HEX = X:010203...08') @click.pass_context def unlock(ctx, key): device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: if key is None: mb.flash_erase_all_unsecure() else: mb.flash_security_disable(key) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() click.echo(" Device unlocked successfully.") # McuBoot: fill memory command @cli.command(short_help="Fill MCU memory with specified pattern") @click.option('-p', '--pattern', type=UInt(), default=0xFFFFFFFF, help='Pattern format (default: 0xFFFFFFFF).') @click.argument('address', type=UInt()) @click.argument('length', type=UInt()) @click.pass_context def fill(ctx, address, length, pattern): device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: mb.fill_memory(address, length, pattern) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() click.secho(f" Memory filled successfully with pattern: 0x{pattern:X}") # McuBoot: reliable update command @cli.command(short_help="Copy backup app from address to main app region") @click.argument('address', type=UInt()) @click.pass_context def update(ctx, address): device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: mb.reliable_update(address) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() # McuBoot: call command @cli.command(short_help="Call code from specified address") @click.argument('address', type=UInt()) @click.argument('argument', type=UInt()) @click.pass_context def call(ctx, address, argument): device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: mb.call(address, argument) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() click.secho(" Code executed") # McuBoot: execute command @cli.command(short_help="Execute code from specified address") @click.argument('address', type=UInt()) @click.argument('argument', type=UInt()) @click.argument('stackpointer', type=UInt()) @click.pass_context def execute(ctx, address, argument, stackpointer): device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: mb.execute(address, argument, stackpointer) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() click.secho(" Code executed") # McuBoot: reset command @cli.command(short_help="Reset MCU") @click.pass_context def reset(ctx): device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: mb.reset(reopen=False) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() click.secho(" Reset success") # McuBoot: Generate key blob command @cli.command(short_help="Generate the Blob for given DEK Key") @click.option('-c', '--count', type=UInt(), default=72, show_default=True, help='Key blob count') @click.argument('dekfile', nargs=1, type=ImgFile('.dek', exists=True)) @click.argument('blobfile', nargs=1, type=ImgFile('.bin')) @click.pass_context def keyblob(ctx, count, dekfile, blobfile): device = scan_interface(ctx.obj['TARGET']) with open(dekfile, "rb") as f: dek_data = f.read() try: with McuBoot(device, True) as mb: blob_data = mb.generate_key_blob(dek_data, count) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() with open(blobfile, "wb") as f: f.write(blob_data) click.echo(f" Successfully saved into: {blobfile}") # McuBoot: Key provisioning command -> Enroll @cli.command(short_help="Key provisioning: Enroll") @click.pass_context def kp_enroll(ctx): device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: mb.kp_enroll() except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() # McuBoot: Key provisioning command -> Generate Intrinsic Key @cli.command(short_help="Key provisioning: Generate Intrinsic Key") @click.argument('key_type', type=UInt()) @click.argument('key_size', type=UInt()) @click.pass_context def kp_gen_key(ctx, key_type, key_size): device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: mb.kp_set_intrinsic_key(key_type, key_size) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() # McuBoot: Key provisioning command -> Send the user key to a bootloader @cli.command(short_help="Key provisioning: Send the user key to a bootloader") @click.argument('key_type', type=UInt()) @click.argument('file', nargs=1, type=ImgFile('.bin', exists=True)) @click.pass_context def kp_user_key(ctx, key_type, file): device = scan_interface(ctx.obj['TARGET']) with open(file, "rb") as f: key_data = f.read() try: with McuBoot(device, True) as mb: mb.kp_set_user_key(key_type, key_data) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() # McuBoot: Key provisioning command -> Write the key into nonvolatile memory @cli.command(short_help="Key provisioning: Write the key into nonvolatile memory") @click.argument('memid', type=UInt()) @click.pass_context def kp_write_nvm(ctx, memid): device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: mb.kp_write_nonvolatile(memid) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() # McuBoot: Key provisioning command -> Read the key from nonvolatile memory @cli.command(short_help="Key provisioning: Read the key from nonvolatile memory") @click.argument('memid', type=UInt()) @click.pass_context def kp_read_nvm(ctx, memid): device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: mb.kp_read_nonvolatile(memid) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() # McuBoot: Key provisioning command -> Write the key into key store area @cli.command(short_help="Key provisioning: Write the key into key store area") @click.argument('key_type', type=UInt()) @click.argument('file', nargs=1, type=ImgFile('.bin', exists=True)) @click.pass_context def kp_write_kstore(ctx, key_type, file): device = scan_interface(ctx.obj['TARGET']) with open(file, "rb") as f: key_data = f.read() try: with McuBoot(device, True) as mb: mb.kp_write_key_store(key_type, key_data) except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() # McuBoot: Key provisioning command -> Read the key from key store area @cli.command(short_help="Key provisioning: Read the key from key store area") @click.option('-f', '--file', type=ImgFile('.bin'), help='Output file name with ext.: *.bin') @click.pass_context def kp_read_kstore(ctx, file): device = scan_interface(ctx.obj['TARGET']) try: with McuBoot(device, True) as mb: key_data = mb.kp_read_key_store() except Exception as e: print_error(str(e), ctx.obj['DEBUG']) if ctx.obj['DEBUG']: click.echo() if file is None: click.echo(hexdump(key_data)) else: with open(file, "wb") as f: f.write(key_data) click.echo(f" Successfully saved into: {file}") def main(): cli(obj={}) if __name__ == '__main__': main() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578307390.0 mboot-0.3.0/mboot/commands.py0000777000175000000000000002226700000000000016430 0ustar00martinroot00000000000000# Copyright (c) 2017 Martin Olejar # # SPDX-License-Identifier: BSD-3-Clause # The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution # or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText from easy_enum import Enum from struct import pack, unpack_from from .errorcodes import StatusCode from .exceptions import McuBootError ######################################################################################################################## # McuBoot Commands and Responses Tags ######################################################################################################################## class CommandTag(Enum): """ McuBoot Commands """ FLASH_ERASE_ALL = (0x01, 'FlashEraseAll', 'Erase Complete Flash') FLASH_ERASE_REGION = (0x02, 'FlashEraseRegion', 'Erase Flash Region') READ_MEMORY = (0x03, 'ReadMemory', 'Read Memory') WRITE_MEMORY = (0x04, 'WriteMemory', 'Write Memory') FILL_MEMORY = (0x05, 'FillMemory', 'Fill Memory') FLASH_SECURITY_DISABLE = (0x06, 'FlashSecurityDisable', 'Disable Flash Security') GET_PROPERTY = (0x07, 'GetProperty', 'Get Property') RECEIVE_SB_FILE = (0x08, 'ReceiveSBFile', 'Receive SB File') EXECUTE = (0x09, 'Execute', 'Execute') CALL = (0x0A, 'Call', 'Call') RESET = (0x0B, 'Reset', 'Reset MCU') SET_PROPERTY = (0x0C, 'SetProperty', 'Set Property') FLASH_ERASE_ALL_UNSECURE = (0x0D, 'FlashEraseAllUnsecure', 'Erase Complete Flash and Unlock') FLASH_PROGRAM_ONCE = (0x0E, 'FlashProgramOnce', 'Flash Program Once') FLASH_READ_ONCE = (0x0F, 'FlashReadOnce', 'Flash Read Once') FLASH_READ_RESOURCE = (0x10, 'FlashReadResource', 'Flash Read Resource') CONFIGURE_MEMORY = (0x11, 'ConfigureMemory', 'Configure Quad-SPI Memory') RELIABLE_UPDATE = (0x12, 'ReliableUpdate', 'Reliable Update') GENERATE_KEY_BLOB = (0x13, 'GenerateKeyBlob', 'Generate Key Blob') KEY_PROVISIONING = (0x15, 'KeyProvisioning', 'Key Provisioning') # reserved commands CONFIGURE_I2C = (0xC1, 'ConfigureI2c', 'Configure I2C') CONFIGURE_SPI = (0xC2, 'ConfigureSpi', 'Configure SPI') CONFIGURE_CAN = (0xC3, 'ConfigureCan', 'Configure CAN') class ResponseTag(Enum): """ McuBoot Responses to Commands """ GENERIC = (0xA0, 'GenericResponse', 'Generic Response') READ_MEMORY = (0xA3, 'ReadMemoryResponse', 'Read Memory Response') GET_PROPERTY = (0xA7, 'GetPropertyResponse', 'Get Property Response') FLASH_READ_ONCE = (0xAF, 'FlashReadOnceResponse', 'Flash Read Once Response') FLASH_READ_RESOURCE = (0xB0, 'FlashReadResourceResponse', 'Flash Read Resource Response') KEY_PROVISIONING_RESPONSE = (0xB5, 'KeyProvisioningResponse', 'Key Provisioning Response') ######################################################################################################################## # McuBoot Command and Response packet classes ######################################################################################################################## class PacketHeader: """ McuBoot command/response packet header """ FORMAT = '4B' SIZE = 4 def __init__(self, tag: int, flags: int, reserved: int, params_count: int): self.tag = tag self.flags = flags self.reserved = reserved self.params_count = params_count def __eq__(self, obj): return isinstance(obj, PacketHeader) and vars(self) == vars(obj) def __str__(self): return f"" def to_bytes(self) -> bytes: """ Serialize header into bytes """ return pack(self.FORMAT, self.tag, self.flags, self.reserved, self.params_count) @classmethod def from_bytes(cls, data: bytes, offset: int = 0): """ Deserialize header from bytes :param data: Input data in bytes :param offset: The offset of input data """ if len(data) < 4: raise McuBootError(f"Invalid format of RX packet (data length is {len(data)} bytes)") return cls(*unpack_from(cls.FORMAT, data, offset)) class CmdPacket: """ McuBoot command packet format class """ SIZE = 32 EMPTY_VALUE = 0x00 def __init__(self, tag: int, flags: int, *args, data=None): assert len(args) < 8 self.header = PacketHeader(tag, flags, 0, len(args)) self.params = list(args) if data is not None: if len(data) % 4: data += b'\0' * (4 - len(data) % 4) self.params.extend(unpack_from(f'<{len(data) // 4}I', data)) self.header.params_count = len(self.params) def __eq__(self, obj): return isinstance(obj, CmdPacket) and self.header == obj.header and self.params == obj.params def __str__(self): tag = CommandTag.get(self.header.tag, f'0x{self.header.tag:02X}') return f"Tag={tag}, Flags=0x{self.header.flags:02X}" + \ "".join(f", P[{n}]=0x{param:08X}" for n, param in enumerate(self.params)) def to_bytes(self, padding: bool = True) -> bytes: """ Serialize CmdPacket into bytes :param padding: If True, add padding to specific size """ self.header.params_count = len(self.params) data = self.header.to_bytes() data += pack(f"<{self.header.params_count}I", *self.params) if padding and len(data) < self.SIZE: data += bytes([self.EMPTY_VALUE] * (self.SIZE - len(data))) return data class CmdResponse: """ McuBoot response base class """ @property def status_code(self): return self.params[0] def __init__(self, header: PacketHeader, params: tuple): self.header = header self.params = params def __bool__(self): return self.status_code == StatusCode.SUCCESS def __str__(self): return f"Tag={ResponseTag[self.header.tag]}" + \ "".join(f", P[{n}]=0x{param:08X}" for n, param in enumerate(self.params)) @classmethod def from_bytes(cls, data: bytes, offset: int = 0): """ Deserialize header from bytes :param data: Input data in bytes :param offset: The offset of input data """ header = PacketHeader.from_bytes(data, offset) offset += PacketHeader.SIZE if header.params_count == 0: raise McuBootError("Invalid params count in header of cmd response packet") if (header.params_count * 4) > (len(data) - offset): raise McuBootError("Invalid params count in header of cmd response packet") return cls(header, unpack_from(f'<{header.params_count}L', data, offset)) class GenericResponse(CmdResponse): """ McuBoot generic response format class """ @property def cmd_tag(self): return self.params[1] def __str__(self): cmd = CommandTag.get(self.cmd_tag, f'Unknown[0x{self.cmd_tag:02X}]') status = StatusCode.get(self.status_code, f'Unknown[0x{self.status_code:08X}]') return f"Tag={ResponseTag[self.header.tag]}, Status={status}, Cmd={cmd}" class GetPropertyResponse(CmdResponse): """ McuBoot get property response format class """ @property def values(self): return self.params[1:] def __str__(self): status = StatusCode.get(self.status_code, f'Unknown[0x{self.status_code:08X}]') return f"Tag={ResponseTag[self.header.tag]}, Status={status}" + \ "".join(f", V[{n}]=0x{value:08X}" for n, value in enumerate(self.values)) class ReadMemoryResponse(CmdResponse): """ McuBoot read memory response format class """ @property def length(self): return self.params[1] def __str__(self): status = StatusCode.get(self.status_code, f'Unknown[0x{self.status_code:08X}]') return f"Tag={ResponseTag[self.header.tag]}, Status={status}, Length={self.length}" class FlashReadOnceResponse(ReadMemoryResponse): """ McuBoot flash read once response format class """ @property def data(self): return pack(f'<{self.header.params_count - 2}L', *self.params[2:]) class FlashReadResourceResponse(ReadMemoryResponse): """ McuBoot flash read resource response format class """ class KeyProvisioningResponse(ReadMemoryResponse): """ McuBoot Key Provisioning response format class """ def parse_cmd_response(data: bytes, offset: int = 0) -> CmdResponse: """ Parse command response :param data: Input data in bytes :param offset: The offset of input data """ known_responses = { ResponseTag.GENERIC: GenericResponse, ResponseTag.GET_PROPERTY: GetPropertyResponse, ResponseTag.READ_MEMORY: ReadMemoryResponse, ResponseTag.FLASH_READ_RESOURCE: FlashReadResourceResponse, ResponseTag.FLASH_READ_ONCE: FlashReadOnceResponse, ResponseTag.KEY_PROVISIONING_RESPONSE: KeyProvisioningResponse } response_tag = data[offset] response_class = known_responses.get(response_tag, CmdResponse) return response_class.from_bytes(data, offset) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1578341034.992349 mboot-0.3.0/mboot/connection/0000777000175000000000000000000000000000000016400 5ustar00martinroot00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578244603.0 mboot-0.3.0/mboot/connection/__init__.py0000777000175000000000000000050200000000000020511 0ustar00martinroot00000000000000# Copyright (c) 2019 Martin Olejar # # SPDX-License-Identifier: BSD-3-Clause # The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution # or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText from .base import DevConnBase from .usb import scan_usb, RawHid ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1576534590.0 mboot-0.3.0/mboot/connection/base.py0000777000175000000000000000150700000000000017672 0ustar00martinroot00000000000000# Copyright (c) 2019 Martin Olejar # # SPDX-License-Identifier: BSD-3-Clause # The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution # or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText from ..commands import CmdPacket, CmdResponse, parse_cmd_response class DevConnBase: @property def is_opened(self): raise NotImplementedError() def __init__(self, **kwargs): self.reopen = kwargs.get('reopen', False) def open(self): raise NotImplementedError() def close(self): raise NotImplementedError() def read(self, timeout=1000): raise NotImplementedError() def write(self, packet): raise NotImplementedError() def info(self): raise NotImplementedError() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1576534590.0 mboot-0.3.0/mboot/connection/uart.py0000777000175000000000000000330300000000000017727 0ustar00martinroot00000000000000# Copyright (c) 2017 Martin Olejar # # SPDX-License-Identifier: BSD-3-Clause # The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution # or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText import sys import glob import serial from time import time from struct import pack, unpack_from from .base import DevConnBase def crc16(data, crc_init=0): """ Calculate 16-bit CRC from input data :param data: :param crc_init: Initialization value :rtype: int value """ crc = crc_init for c in data: crc ^= c << 8 for _ in range(8): temp = crc << 1 if crc & 0x8000: temp ^= 0x1021 crc = temp return crc def scan_uart(port): raise NotImplemented("Function is not implemented") ######################################################################################################################## # UART Interface Class ######################################################################################################################## class FPType: # MBoot Framing Packet Type. ACK = 0xA1 NACK = 0xA2 ABORT = 0xA3 CMD = 0xA4 DATA = 0xA5 PING = 0xA6 PINGR = 0xA7 class Uart(DevConnBase): @property def is_opened(self): return False def __init__(self, **kwargs): super().__init__(**kwargs) def open(self, port=None): pass def close(self): pass def info(self): pass def read(self, timeout=1000, length=None, cmd=False): pass def write(self, data, cmd=False): pass ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578182499.0 mboot-0.3.0/mboot/connection/usb.py0000777000175000000000000003244100000000000017552 0ustar00martinroot00000000000000# Copyright (c) 2017 Martin Olejar # # SPDX-License-Identifier: BSD-3-Clause # The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution # or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText import os import logging import collections from time import time from struct import pack, unpack_from from .base import DevConnBase, CmdPacket, parse_cmd_response logger = logging.getLogger('MBOOT:USB') # os.environ['PYUSB_DEBUG'] = 'debug' # os.environ['PYUSB_LOG_FILENAME'] = 'usb.log' ######################################################################################################################## # Devices ######################################################################################################################## USB_DEVICES = { # NAME | VID | PID 'MKL27': (0x15A2, 0x0073), 'LPC55': (0x1FC9, 0x0021), 'IMXRT': (0x1FC9, 0x0135) } def scan_usb(device_name: str = None) -> list: """ Scan connected USB devices :param device_name: The specific device name (MKL27, LPC55, ...) or VID:PID """ devices = [] if device_name is None: for name, value in USB_DEVICES.items(): devices += RawHid.enumerate(value[0], value[1]) else: if ':' in device_name: vid, pid = device_name.split(':') devices = RawHid.enumerate(int(vid, 0), int(pid, 0)) else: if device_name in USB_DEVICES: vid = USB_DEVICES[device_name][0] pid = USB_DEVICES[device_name][1] devices = RawHid.enumerate(vid, pid) return devices ######################################################################################################################## # USB HID Interface Base Class ######################################################################################################################## REPORT_ID = { # USB HID Reports 'CMD_OUT': 0x01, 'CMD_IN': 0x03, 'DATA_OUT': 0x02, 'DATA_IN': 0x04 } class RawHidBase(DevConnBase): @property def is_opened(self): return self._opened def __init__(self, **kwargs): super().__init__(**kwargs) self._opened = False self.vid = 0 self.pid = 0 self.vendor_name = "" self.product_name = "" @staticmethod def _encode_report(report_id, report_size, data, offset=0): data_len = min(len(data) - offset, report_size - 4) raw_data = pack('<2BH', report_id, 0x00, data_len) raw_data += data[offset: offset + data_len] raw_data += bytes([0x00] * (report_size - len(raw_data))) logger.debug(f"OUT[{len(raw_data)}]: " + ' '.join(f"{b:02X}" for b in raw_data)) return raw_data, offset + data_len @staticmethod def _decode_report(raw_data): logger.debug(f"IN [{len(raw_data)}]: " + ' '.join(f"{b:02X}" for b in raw_data)) report_id, _, plen = unpack_from('<2BH', raw_data) data = bytes(raw_data[4: 4 + plen]) if report_id == REPORT_ID['CMD_IN']: return parse_cmd_response(data) return data def open(self): raise NotImplementedError() def close(self): raise NotImplementedError() def read(self, timeout=1000): raise NotImplementedError() def write(self, packet): raise NotImplementedError() def info(self): return f"{self.product_name:s} (0x{self.vid:04X}, 0x{self.pid:04X})" ######################################################################################################################## # USB Interface Classes ######################################################################################################################## if os.name == "nt": try: import pywinusb.hid as hid except: raise Exception("PyWinUSB is required on a Windows Machine") class RawHid(RawHidBase): """ This class provides basic functions to access a USB HID device using pywinusb: - write/read an endpoint """ def __init__(self, **kwargs): super().__init__(**kwargs) # Vendor page and usage_id = 2 self.report = [] # deque used here instead of synchronized Queue # since read speeds are ~10-30% faster and are # comparable to a based list implementation. self.rcv_data = collections.deque() self.device = None return # handler called when a report is received def __rx_handler(self, data): # logging.debug("rcv: %s", data[1:]) self.rcv_data.append(data) def open(self): """ open the interface """ logger.debug("Open Interface") self.device.set_raw_data_handler(self.__rx_handler) self.device.open(shared=False) self._opened = True def close(self): """ close the interface """ logger.debug("Close Interface") self.device.close() self._opened = False def write(self, packet): """ Write data on the OUT endpoint associated to the HID interface :param packet: HID packet data """ if isinstance(packet, CmdPacket): report_id = REPORT_ID['CMD_OUT'] data = packet.to_bytes() elif isinstance(packet, (bytes, bytearray)): report_id = REPORT_ID['DATA_OUT'] data = packet else: raise Exception() data_index = 0 report_size = self.report[report_id - 1]._HidReport__raw_report_size while data_index < len(data): raw_data, data_index = self._encode_report(report_id, report_size, data, data_index) self.report[report_id - 1].send(raw_data) def read(self, timeout=2000): """ Read data from IN endpoint associated to the HID interface :param timeout: """ start = time() while len(self.rcv_data) == 0: if ((time() - start) * 1000) > timeout: raise TimeoutError() raw_data = self.rcv_data.popleft() return self._decode_report(bytes(raw_data)) @staticmethod def enumerate(vid, pid): """ Get an array of all connected devices which matches PyWinUSB.vid/PyWinUSB.pid. :param vid: USB Vendor ID :param pid: USB Product ID """ targets = [] all_devices = hid.find_all_hid_devices() # find devices with good vid/pid for dev in all_devices: if (dev.vendor_id == vid) and (dev.product_id == pid): try: dev.open(shared=False) report = dev.find_output_reports() if report: new_target = RawHid() new_target.report = report new_target.vendor_name = dev.vendor_name new_target.product_name = dev.product_name new_target.vid = dev.vendor_id new_target.pid = dev.product_id new_target.device = dev new_target.device.set_raw_data_handler(new_target.__rx_handler) targets.append(new_target) except Exception as e: logger.error("Receiving Exception: %s", str(e)) finally: dev.close() return targets else: try: import usb.core import usb.util except: raise Exception("PyUSB is required on a Linux Machine") class RawHid(RawHidBase, DevConnBase): """ This class provides basic functions to access a USB HID device using pyusb: - write/read an endpoint """ def __init__(self): super().__init__() self.ep_out = None self.ep_in = None self.device = None self.interface_number = -1 def open(self): """ open the interface """ logger.debug("Open Interface") self._opened = True def close(self): """ close the interface """ logger.debug("Close Interface") self._opened = False try: if self.device: usb.util.dispose_resources(self.device) except: pass def write(self, packet): """ Write data on the OUT endpoint associated to the HID interface :param packet: HID packet data """ if isinstance(packet, CmdPacket): report_id = REPORT_ID['CMD_OUT'] data = packet.to_bytes() elif isinstance(packet, (bytes, bytearray)): report_id = REPORT_ID['DATA_OUT'] data = packet else: raise Exception() data_index = 0 if self.ep_out: report_size = self.ep_out.wMaxPacketSize while data_index < len(data): raw_data, data_index = self._encode_report(report_id, report_size, data, data_index) self.ep_out.write(raw_data) else: bmRequestType = 0x21 # Host to device request of type Class of Recipient Interface bmRequest = 0x09 # Set_REPORT (HID class-specific request for transferring data over EP0) wValue = 0x200 + report_id # Issuing an OUT report with specified ID wIndex = self.interface_number # Interface number for HID report_size = 36 # TODO: get the value from descriptor while data_index < len(data): raw_data, data_index = self._encode_report(report_id, report_size, data, data_index) self.device.ctrl_transfer(bmRequestType, bmRequest, wValue, wIndex, raw_data) def read(self, timeout=1000): """ Read data from IN endpoint associated to the HID interface :param timeout: """ # TODO: test if self.ep_in.wMaxPacketSize is accessible in all Linux distributions raw_data = self.ep_in.read(self.ep_in.wMaxPacketSize, timeout) return self._decode_report(raw_data) @staticmethod def enumerate(vid, pid): """ Get list of all connected devices which matches PyUSB.vid and PyUSB.pid. :param vid: USB Vendor ID :param pid: USB Product ID """ # find all devices matching the vid/pid specified all_devices = usb.core.find(find_all=True, idVendor=vid, idProduct=pid) if not all_devices: logger.debug("No device connected") return None targets = [] # iterate on all devices found for dev in all_devices: interface = None interface_number = -1 # get active config config = dev.get_active_configuration() # iterate on all interfaces: for interface in config: if interface.bInterfaceClass == 0x03: # HID Interface interface_number = interface.bInterfaceNumber break if interface is None or interface_number == -1: continue try: if dev.is_kernel_driver_active(interface_number): dev.detach_kernel_driver(interface_number) except Exception as e: print(str(e)) try: dev.set_configuration() dev.reset() except usb.core.USBError as e: logger.debug(f"Cannot set configuration for the device: {str(e)}") ep_in, ep_out = None, None for ep in interface: if ep.bEndpointAddress & 0x80: ep_in = ep else: ep_out = ep if not ep_in: logger.error('Endpoints not found') return None new_target = RawHid() new_target.ep_in = ep_in new_target.ep_out = ep_out new_target.device = dev new_target.vid = vid new_target.pid = pid new_target.interface_number = interface_number new_target.vendor_name = usb.util.get_string(dev, 1).strip('\0') new_target.product_name = usb.util.get_string(dev, 2).strip('\0') targets.append(new_target) return targets ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1576533945.0 mboot-0.3.0/mboot/errorcodes.py0000777000175000000000000001631000000000000016766 0ustar00martinroot00000000000000# Copyright (c) 2017 Martin Olejar # # SPDX-License-Identifier: BSD-3-Clause # The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution # or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText from easy_enum import Enum ######################################################################################################################## # McuBoot Status Codes (Errors) ######################################################################################################################## class StatusCode(Enum): """ McuBoot status codes """ SUCCESS = (0, 'Success', 'Success') FAIL = (1, 'Fail', 'Fail') READ_ONLY = (2, 'ReadOnly', 'Read Only Error') OUT_OF_RANGE = (3, 'OutOfRange', 'Out Of Range Error') INVALID_ARGUMENT = (4, 'InvalidArgument', 'Invalid Argument Error') TIMEOUT = (5, 'TimeoutError', 'Timeout Error') NO_TRANSFER_IN_PROGRESS = (6, 'NoTransferInProgress', 'No Transfer In Progress Error') # Flash driver errors. FLASH_SIZE_ERROR = (100, 'FlashSizeError', 'FLASH Driver: Size Error') FLASH_ALIGNMENT_ERROR = (101, 'FlashAlignmentError', 'FLASH Driver: Alignment Error') FLASH_ADDRESS_ERROR = (102, 'FlashAddressError', 'FLASH Driver: Address Error') FLASH_ACCESS_ERROR = (103, 'FlashAccessError', 'FLASH Driver: Access Error') FLASH_PROTECTION_VIOLATION = (104, 'FlashProtectionViolation', 'FLASH Driver: Protection Violation') FLASH_COMMAND_FAILURE = (105, 'FlashCommandFailure', 'FLASH Driver: Command Failure') FLASH_UNKNOWN_PROPERTY = (106, 'FlashUnknownProperty', 'FLASH Driver: Unknown Property') FLASH_REGION_EXECUTE_ONLY = (108, 'FlashRegionExecuteOnly', 'FLASH Driver: Region Execute Only') FLASH_EXEC_IN_RAM_NOT_READY = (109, 'FlashExecuteInRamFunctionNotReady', 'FLASH Driver: Execute In RAM Function Not Ready') FLASH_COMMAND_NOT_SUPPORTED = (111, 'FlashCommandNotSupported', 'FLASH Driver: Command Not Supported') FLASH_OUT_OF_DATE_CFPA_PAGE = (132, 'FlashOutOfDateCfpaPage', 'FLASH Driver: Out Of Date CFPA Page') # I2C driver errors. I2C_SLAVE_TX_UNDERRUN = (200, 'I2cSlaveTxUnderrun', 'I2C Driver: Slave Tx Underrun') I2C_SLAVE_RX_OVERRUN = (201, 'I2cSlaveRxOverrun', 'I2C Driver: Slave Rx Overrun') I2C_ARBITRATION_LOST = (202, 'I2cArbitrationLost', 'I2C Driver: Arbitration Lost') # SPI driver errors. SPI_SLAVE_TX_UNDERRUN = (300, 'SpiSlaveTxUnderrun', 'SPI Driver: Slave Tx Underrun') SPI_SLAVE_RX_OVERRUN = (301, 'SpiSlaveRxOverrun', 'SPI Driver: Slave Rx Overrun') # QuadSPI driver errors. QSPI_FLASH_SIZE_ERROR = (400, 'QspiFlashSizeError', 'QSPI Driver: Flash Size Error') QSPI_FLASH_ALIGNMENT_ERROR = (401, 'QspiFlashAlignmentError', 'QSPI Driver: Flash Alignment Error') QSPI_FLASH_ADDRESS_ERROR = (402, 'QspiFlashAddressError', 'QSPI Driver: Flash Address Error') QSPI_FLASH_COMMAND_FAILURE = (403, 'QspiFlashCommandFailure', 'QSPI Driver: Flash Command Failure') QSPI_FLASH_UNKNOWN_PROPERTY = (404, 'QspiFlashUnknownProperty', 'QSPI Driver: Flash Unknown Property') QSPI_NOT_CONFIGURED = (405, 'QspiNotConfigured', 'QSPI Driver: Not Configured') QSPI_COMMAND_NOT_SUPPORTED = (406, 'QspiCommandNotSupported', 'QSPI Driver: Command Not Supported') QSPI_COMMAND_TIMEOUT = (407, 'QspiCommandTimeout', 'QSPI Driver: Command Timeout') QSPI_WRITE_FAILURE = (408, 'QspiWriteFailure', 'QSPI Driver: Write Failure') # OTFAD driver errors. OTFAD_SECURITY_VIOLATION = (500, 'OtfadSecurityViolation', 'OTFAD Driver: Security Violation') OTFAD_LOGICALLY_DISABLED = (501, 'OtfadLogicallyDisabled', 'OTFAD Driver: Logically Disabled') OTFAD_INVALID_KEY = (502, 'OtfadInvalidKey', 'OTFAD Driver: Invalid Key') OTFAD_INVALID_KEY_BLOB = (503, 'OtfadInvalidKeyBlob', 'OTFAD Driver: Invalid Key Blob') # SDMMC driver errors. # Bootloader errors. UNKNOWN_COMMAND = (10000, 'UnknownCommand', 'Unknown Command') SECURITY_VIOLATION = (10001, 'SecurityViolation', 'Security Violation') ABORT_DATA_PHASE = (10002, 'AbortDataPhase', 'Abort Data Phase') PING_ERROR = (10003, 'PingError', 'Ping Error') NO_RESPONSE = (10004, 'NoResponse', 'No Response') NO_RESPONSE_EXPECTED = (10005, 'NoResponseExpected', 'No Response Expected') UNSUPPORTED_COMMAND = (10006, 'UnsupportedCommand', 'Unsupported Command') # SB loader errors. ROMLDR_SECTION_OVERRUN = (10100, 'RomLdrSectionOverrun', 'ROM Loader: Section Overrun') ROMLDR_SIGNATURE = (10101, 'RomLdrSignature', 'ROM Loader: Signature Error') ROMLDR_SECTION_LENGTH = (10102, 'RomLdrSectionLength', 'ROM Loader: Section Length Error') ROMLDR_UNENCRYPTED_ONLY = (10103, 'RomLdrUnencryptedOnly', 'ROM Loader: Unencrypted Only') ROMLDR_EOF_REACHED = (10104, 'RomLdrEOFReached', 'ROM Loader: EOF Reached') ROMLDR_CHECKSUM = (10105, 'RomLdrChecksum', 'ROM Loader: Checksum Error') ROMLDR_CRC32_ERROR = (10106, 'RomLdrCrc32Error', 'ROM Loader: CRC32 Error') ROMLDR_UNKNOWN_COMMAND = (10107, 'RomLdrUnknownCommand', 'ROM Loader: Unknown Command') ROMLDR_ID_NOT_FOUND = (10108, 'RomLdrIdNotFound', 'ROM Loader: ID Not Found') ROMLDR_DATA_UNDERRUN = (10109, 'RomLdrDataUnderrun', 'ROM Loader: Data Underrun') ROMLDR_JUMP_RETURNED = (10110, 'RomLdrJumpReturned', 'ROM Loader: Jump Returned') ROMLDR_CALL_FAILED = (10111, 'RomLdrCallFailed', 'ROM Loader: Call Failed') ROMLDR_KEY_NOT_FOUND = (10112, 'RomLdrKeyNotFound', 'ROM Loader: Key Not Found') ROMLDR_SECURE_ONLY = (10113, 'RomLdrSecureOnly', 'ROM Loader: Secure Only') ROMLDR_RESET_RETURNED = (10114, 'RomLdrResetReturned', 'ROM Loader: Reset Returned') ROMLDR_ROLLBACK_BLOCKED = (10115, 'RomLdrRollbackBlocked', 'ROM Loader: Rollback Blocked') ROMLDR_INVALID_SECTION_MAC_COUNT = (10116, 'RomLdrInvalidSectionMacCount', 'ROM Loader: Invalid Section Mac Count') ROMLDR_UNEXPECTED_COMMAND = (10117, 'RomLdrUnexpectedCommand', 'ROM Loader: Unexpected Command') # Memory interface errors. MEMORY_RANGE_INVALID = (10200, 'MemoryRangeInvalid', 'Memory Range Invalid') MEMORY_READ_FAILED = (10201, 'MemoryReadFailed', 'Memory Read Failed') MEMORY_WRITE_FAILED = (10202, 'MemoryWriteFailed', 'Memory Write Failed') MEMORY_CUMULATIVE_WRITE = (10203, 'MemoryCumulativeWrite', 'Memory Cumulative Write') MEMORY_NOT_CONFIGURED = (10205, 'MemoryNotConfigured', 'Memory Not Configured') # Property store errors. UNKNOWN_PROPERTY = (10300, 'UnknownProperty', 'Unknown Property') READ_ONLY_PROPERTY = (10301, 'ReadOnlyProperty', 'Read Only Property') INVALID_PROPERTY_VALUE = (10302, 'InvalidPropertyValue', 'Invalid Property Value') # CRC check errors. APP_CRC_CHECK_PASSED = (10400, 'AppCrcCheckPassed', 'Application CRC Check: Passed') APP_CRC_CHECK_FAILED = (10401, 'AppCrcCheckFailed', 'Application: CRC Check: Failed') APP_CRC_CHECK_INACTIVE = (10402, 'AppCrcCheckInactive', 'Application CRC Check: Inactive') APP_CRC_CHECK_INVALID = (10403, 'AppCrcCheckInvalid', 'Application CRC Check: Invalid') APP_CRC_CHECK_OUT_OF_RANGE = (10404, 'AppCrcCheckOutOfRange', 'Application CRC Check: Out Of Range') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578261736.0 mboot-0.3.0/mboot/exceptions.py0000777000175000000000000000275300000000000017006 0ustar00martinroot00000000000000# Copyright (c) 2019 Martin Olejar # # SPDX-License-Identifier: BSD-3-Clause # The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution # or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText from .errorcodes import StatusCode ######################################################################################################################## # McuBoot Exceptions ######################################################################################################################## class McuBootError(Exception): """ MBoot Module: Base Exception """ fmt = 'MBoot ERROR: {description}' def __init__(self, desc=None): self.description = "Unknown Error" if desc is None else desc def __str__(self): return self.fmt.format(description=self.description) class McuBootCommandError(McuBootError): """ MBoot Module: Command Exception """ fmt = 'MBoot ERROR: {cmd_name} interrupted -> {description}' def __init__(self, cmd, value): self.cmd_name = cmd self.error_value = value self.description = StatusCode.desc(value, f"Unknown Error 0x{value:08X}") def __str__(self): return self.fmt.format(cmd_name=self.cmd_name, description=self.description) class McuBootConnectionError(McuBootError): """ MBoot Module: Connection Exception """ fmt = 'MBoot ERROR: Connection issue -> {description}' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578312831.0 mboot-0.3.0/mboot/mcuboot.py0000777000175000000000000007243200000000000016276 0ustar00martinroot00000000000000# Copyright (c) 2017 Martin Olejar # # SPDX-License-Identifier: BSD-3-Clause # The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution # or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText from time import sleep from typing import Optional from logging import getLogger from easy_enum import Enum # internal from .commands import CommandTag, CmdPacket, CmdResponse, GenericResponse from .memories import ExtMemPropTags, ExtMemId from .properties import PropertyTag, Version, parse_property_value from .exceptions import McuBootError, McuBootCommandError, McuBootConnectionError from .errorcodes import StatusCode from .connection import DevConnBase ######################################################################################################################## # McuBoot Logger Name ######################################################################################################################## logger = getLogger('MBOOT') ######################################################################################################################## # McuBoot Tags for Key Provisioning Operations ######################################################################################################################## class KeyProvOperation(Enum): ENROLL = (0, 'Enroll', 'Enroll Operation') SET_USER_KEY = (1, 'SetUserKey', 'Set User Key Operation') SET_INTRINSIC_KEY = (2, 'SetIntrinsicKey', 'Set Intrinsic Key Operation') WRITE_NON_VOLATILE = (3, 'WriteNonVolatile', 'Write Non Volatile Operation') READ_NON_VOLATILE = (4, 'ReadNonVolatile', 'Read Non Volatile Operation') WRITE_KEY_STORE = (5, 'WriteKeyStore', 'Write Key Store Operation') READ_KEY_STORE = (6, 'ReadKeyStore', 'Read Key Store Operation') ######################################################################################################################## # McuBoot Main Class ######################################################################################################################## class McuBoot: @property def status_code(self): return self._status_code @property def status_info(self): return StatusCode.get(self.status_code, f'Unknown[0x{self.status_code:08X}]') @property def is_opened(self): return self._device.is_opened def __init__(self, device: DevConnBase, cmd_exception: bool = False): """ Initialize the McuBoot object. :param device: The instance of communication interface class :param cmd_exception: """ self._cmd_exception = cmd_exception self._status_code = StatusCode.SUCCESS self._device = device self.reopen = False def __enter__(self): self.reopen = True self.open() return self def __exit__(self, *args, **kwargs): self.close() def _check_response(self, cmd_packet: CmdPacket, cmd_response: CmdResponse, logger_info: bool = True): cmd_name = CommandTag[cmd_packet.header.tag] if not isinstance(cmd_response, CmdResponse): raise McuBootError(f"CMD: {cmd_name} -> Unsupported response format") self._status_code = cmd_response.status_code if self._status_code == StatusCode.SUCCESS: if logger_info: logger.info("CMD: Done successfully") return True logger.info(f"CMD: {cmd_name} Error -> " + self.status_info) if self._cmd_exception: raise McuBootCommandError(cmd_name, self.status_code) return False def _process_cmd(self, cmd_packet: CmdPacket, timeout: int = 2000): """ Process Command :param cmd_packet: Command Packet :param timeout: The maximal waiting time in [ms] for response packet :return: CmdResponse """ if not self._device.is_opened: logger.info('TX: Device not opened') raise McuBootConnectionError('Device not opened') logger.debug('TX-PACKET: ' + str(cmd_packet)) try: self._device.write(cmd_packet) cmd_response = self._device.read(timeout) except TimeoutError: self._status_code = StatusCode.NO_RESPONSE logger.debug('RX-PACKET: No Response, Timeout Error !') raise McuBootConnectionError("No Response from Device") logger.debug('RX-PACKET: ' + str(cmd_response)) return cmd_response def _read_data(self, cmd_tag: int, length: int, timeout: int = 1000) -> bytes: """ Read Data :param cmd_tag: :param length: :param timeout: """ data = b'' if not self._device.is_opened: logger.info('RX: Device not opened') raise McuBootConnectionError('Device not opened') while True: try: response = self._device.read(timeout) except TimeoutError: self._status_code = StatusCode.NO_RESPONSE logger.debug('RX: No Response, Timeout Error !') raise McuBootConnectionError("No Response from Device") if isinstance(response, bytes): data += response elif isinstance(response, GenericResponse): logger.debug('RX-PACKET: ' + str(response)) self._status_code = response.status_code if response.cmd_tag == cmd_tag: break if len(data) < length or self.status_code != StatusCode.SUCCESS: logger.debug(f"CMD: Received {len(data)} from {length} Bytes, {self.status_info}") if self._cmd_exception: raise McuBootCommandError(CommandTag[cmd_tag], self.status_code) else: logger.info(f"CMD: Successfully Received {len(data)} from {length} Bytes") return data[:length] if len(data) > length else data def _send_data(self, cmd_tag: int, data: bytes) -> bool: """ Send Data part of specific command :param cmd_tag: The command tag :param data: Data in bytes """ if not self._device.is_opened: logger.info('TX: Device Disconnected') raise McuBootConnectionError('Device Disconnected !') try: self._device.write(data) response = self._device.read() except TimeoutError: self._status_code = StatusCode.NO_RESPONSE logger.debug('RX: No Response, Timeout Error !') raise McuBootConnectionError("No Response from Device") # TODO: Check response type if needed # if not isinstance(response, GenericResponse): logger.debug('RX-PACKET: ' + str(response)) self._status_code = response.status_code if response.status_code != StatusCode.SUCCESS: logger.debug("CMD: Send Error, " + self.status_info) if self._cmd_exception: raise McuBootCommandError(CommandTag[cmd_tag], self.status_code) return False logger.info(f"CMD: Successfully Send {len(data)} Bytes") return True def open(self): """ Connect to device """ if not self._device.is_opened: logger.info('Connect: %s', self._device.info()) self._device.open() def close(self): """ Disconnect device """ self._device.close() def get_property_list(self) -> list: """ Get list of available properties :return: list """ property_list = [] for _, tag, _ in PropertyTag: try: values = self.get_property(tag) except McuBootCommandError: continue if values: property_list.append(parse_property_value(tag, values)) self._status_code = StatusCode.SUCCESS if not property_list: self._status_code = StatusCode.FAIL if self._cmd_exception: raise McuBootCommandError('GetPropertyList', self.status_code) return property_list def get_memory_list(self) -> dict: """ Get list of embedded memories :return: dict """ memory_list = {} # Internal FLASH index = 0 mdata: dict = {} start_address = 0 while True: try: values = self.get_property(PropertyTag.FLASH_START_ADDRESS, index) if not values: break if index == 0: start_address = values[0] elif start_address == values[0]: break mdata[index] = {} mdata[index]['address'] = values[0] values = self.get_property(PropertyTag.FLASH_SIZE, index) if not values: break mdata[index]['size'] = values[0] values = self.get_property(PropertyTag.FLASH_SECTOR_SIZE, index) if not values: break mdata[index]['sector_size'] = values[0] index += 1 except McuBootCommandError: break if mdata: memory_list['internal_flash'] = mdata # Internal RAM index = 0 mdata = {} start_address = 0 while True: try: values = self.get_property(PropertyTag.RAM_START_ADDRESS, index) if not values: break if index == 0: start_address = values[0] elif start_address == values[0]: break mdata[index] = {} mdata[index]['address'] = values[0] values = self.get_property(PropertyTag.RAM_SIZE, index) if not values: break mdata[index]['size'] = values[0] index += 1 except McuBootCommandError: break if mdata: memory_list['internal_ram'] = mdata # External Memories ext_mem_list = [] ext_mem_ids = [mem_id for _, mem_id, _ in ExtMemId] try: values = self.get_property(PropertyTag.CURRENT_VERSION) except McuBootCommandError: values = None if not values and self._status_code == StatusCode.UNKNOWN_PROPERTY: self._status_code = StatusCode.SUCCESS if not memory_list: self._status_code = StatusCode.FAIL if self._cmd_exception: raise McuBootCommandError('GetMemoryList', self.status_code) return memory_list if Version(values[0]) <= Version("2.0.0"): # old versions mboot support only Quad SPI memory ext_mem_ids = [ExtMemId.QUAD_SPI0] for id in ext_mem_ids: mem_attrs = {} try: values = self.get_property(PropertyTag.EXTERNAL_MEMORY_ATTRIBUTES, id) except McuBootCommandError: values = None if not values: if self._status_code == StatusCode.UNKNOWN_PROPERTY: # No external memories are supported by current device. break elif self._status_code == StatusCode.INVALID_ARGUMENT: # Current memory type is not supported by the device, skip to next external memory. continue elif self._status_code == StatusCode.QSPI_NOT_CONFIGURED: # QSPI0 is not supported, skip to next external memory. continue elif self._status_code == StatusCode.MEMORY_NOT_CONFIGURED: # Un-configured external memory, skip to next external memory. continue elif self._status_code != StatusCode.SUCCESS: # Other Error break # memory ID and name mem_attrs['mem_id'] = id mem_attrs['mem_name'] = ExtMemId[id] # parse memory attributes if values[0] & ExtMemPropTags.START_ADDRESS: mem_attrs['address'] = values[1] if values[0] & ExtMemPropTags.SIZE_IN_KBYTES: mem_attrs['size'] = values[2] * 1024 if values[0] & ExtMemPropTags.PAGE_SIZE: mem_attrs['page_size'] = values[3] if values[0] & ExtMemPropTags.SECTOR_SIZE: mem_attrs['sector_size'] = values[4] if values[0] & ExtMemPropTags.BLOCK_SIZE: mem_attrs['block_size'] = values[5] # store attributes ext_mem_list.append(mem_attrs) if ext_mem_list: memory_list['external'] = ext_mem_list self._status_code = StatusCode.SUCCESS if not memory_list: self._status_code = StatusCode.FAIL if self._cmd_exception: raise McuBootCommandError('GetMemoryList', self.status_code) return memory_list def flash_erase_all(self, mem_id: int = 0) -> bool: """ Erase complete flash memory without recovering flash security section :param mem_id: Memory ID """ logger.info(f"CMD: FlashEraseAll(mem_id={mem_id})") cmd_packet = CmdPacket(CommandTag.FLASH_ERASE_ALL, 0, mem_id) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def flash_erase_region(self, address: int, length: int, mem_id: int = 0) -> bool: """ Erase specified range of flash :param address: Start address :param length: Count of bytes :param mem_id: Memory ID """ logger.info(f"CMD: FlashEraseRegion(address=0x{address:08X}, length={length}, mem_id={mem_id})") cmd_packet = CmdPacket(CommandTag.FLASH_ERASE_REGION, 0, address, length, mem_id) cmd_response = self._process_cmd(cmd_packet, 5000) return self._check_response(cmd_packet, cmd_response) def read_memory(self, address: int, length: int, mem_id: int = 0) -> Optional[bytes]: """ Read data from MCU memory :param address: Start address :param length: Count of bytes :param mem_id: Memory ID """ logger.info(f"CMD: ReadMemory(address=0x{address:08X}, length={length}, mem_id={mem_id})") cmd_packet = CmdPacket(CommandTag.READ_MEMORY, 0, address, length, mem_id) cmd_response = self._process_cmd(cmd_packet) if self._check_response(cmd_packet, cmd_response, False): return self._read_data(CommandTag.READ_MEMORY, cmd_response.length) return None def write_memory(self, address: int, data: bytes, mem_id: int = 0) -> bool: """ Write data into MCU memory :param address: Start address :param data: List of bytes :param mem_id: Memory ID """ logger.info(f"CMD: WriteMemory(address=0x{address:08X}, length={len(data)}, mem_id={mem_id})") cmd_packet = CmdPacket(CommandTag.WRITE_MEMORY, 0, address, len(data), mem_id) cmd_response = self._process_cmd(cmd_packet) if self._check_response(cmd_packet, cmd_response, False): return self._send_data(CommandTag.WRITE_MEMORY, data) return False def fill_memory(self, address: int, length: int, pattern: int = 0xFFFFFFFF) -> bool: """ Fill MCU memory with specified pattern :param address: Start address (must be word aligned) :param length: Count of words (must be word aligned) :param pattern: Count of wrote bytes """ logger.info(f"CMD: FillMemory(address=0x{address:08X}, length={length}, pattern=0x{pattern:08X})") cmd_packet = CmdPacket(CommandTag.FILL_MEMORY, 0, address, length, pattern) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def flash_security_disable(self, backdoor_key: bytes) -> bool: """ Disable flash security by using of backdoor key :param backdoor_key: The key value as array of 8 bytes """ if len(backdoor_key) != 8: raise ValueError('Backdoor key must by 8 bytes long') logger.info(f"CMD: FlashSecurityDisable(backdoor_key={backdoor_key})") cmd_packet = CmdPacket(CommandTag.FLASH_SECURITY_DISABLE, 0, data=backdoor_key) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def get_property(self, prop_tag: int, index: int = 0) -> Optional[list]: """ Get specified property value :param prop_tag: Property TAG (see Properties Enum) :param index: External memory ID or internal memory region index (depends on property type) """ logger.info(f"CMD: GetProperty({PropertyTag[prop_tag]}, index={index})") cmd_packet = CmdPacket(CommandTag.GET_PROPERTY, 0, prop_tag, index) cmd_response = self._process_cmd(cmd_packet) if self._check_response(cmd_packet, cmd_response): return cmd_response.values return None def set_property(self, prop_tag: int, value: int) -> bool: """ Set value of specified property :param prop_tag: Property TAG (see Property enumerator) :param value: The value of selected property """ logger.info(f"CMD: SetProperty({PropertyTag[prop_tag]}, value=0x{value:08X})") cmd_packet = CmdPacket(CommandTag.SET_PROPERTY, 0, prop_tag, value) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def receive_sb_file(self, data: bytes) -> bool: """ Receive SB file :param data: SB file data """ logger.info(f"CMD: ReceiveSBfile(data_length={len(data)})") cmd_packet = CmdPacket(CommandTag.RECEIVE_SB_FILE, 1, len(data)) cmd_response = self._process_cmd(cmd_packet) if self._check_response(cmd_packet, cmd_response, False): return self._send_data(CommandTag.RECEIVE_SB_FILE, data) return False def execute(self, address: int, argument: int, sp: int) -> bool: """ Fill MCU memory with specified pattern :param address: Jump address (must be word aligned) :param argument: Function arguments address :param sp: Stack pointer address """ logger.info(f"CMD: Execute(address=0x{address:08X}, argument=0x{argument:08X}, SP=0x{sp:08X})") cmd_packet = CmdPacket(CommandTag.EXECUTE, 0, address, argument, sp) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def call(self, address: int, argument: int) -> bool: """ Fill MCU memory with specified pattern :param address: Call address (must be word aligned) :param argument: Function arguments address """ logger.info(f"CMD: Call(address=0x{address:08X}, argument=0x{argument:08X})") cmd_packet = CmdPacket(CommandTag.CALL, 0, address, argument) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def reset(self, timeout: int = 2000, reopen: bool = True) -> bool: """ Reset MCU and reconnect if enabled :param timeout: The maximal waiting time in [ms] for reopen connection :param reopen: True for reopen connection after HW reset else False """ ret_val = False logger.info('CMD: Reset MCU') cmd_packet = CmdPacket(CommandTag.RESET, 0) cmd_response = self._process_cmd(cmd_packet) if self._check_response(cmd_packet, cmd_response): self._device.close() ret_val = True if self.reopen and reopen: sleep(timeout / 1000) try: self._device.open() except: ret_val = False if self._cmd_exception: raise McuBootConnectionError() return ret_val def flash_erase_all_unsecure(self) -> bool: """ Erase complete flash memory and recover flash security section :return bool """ logger.info('CMD: FlashEraseAllUnsecure') cmd_packet = CmdPacket(CommandTag.FLASH_ERASE_ALL_UNSECURE, 0) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def efuse_read_once(self, index: int) -> Optional[int]: """ Read from MCU flash program once region (max 8 bytes) :param index: Start index """ logger.info(f"CMD: FlashReadOnce(index={index})") cmd_packet = CmdPacket(CommandTag.FLASH_READ_ONCE, 0, index, 4) cmd_response = self._process_cmd(cmd_packet) return cmd_response.values[0] if self._check_response(cmd_packet, cmd_response) else None def efuse_program_once(self, index: int, value: int) -> bool: """ Write into MCU once program region :param index: Start index :param value: Int value (4 bytes long) """ logger.info(f"CMD: FlashProgramOnce(index={index}, value=0x{value:X})") cmd_packet = CmdPacket(CommandTag.FLASH_PROGRAM_ONCE, 0, index, 4, value) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def flash_read_once(self, index: int, count: int = 4) -> Optional[bytes]: """ Read from MCU flash program once region (max 8 bytes) :param index: Start index :param count: Count of bytes """ assert count in (4, 8) logger.info(f"CMD: FlashReadOnce(index={index}, bytes={count})") cmd_packet = CmdPacket(CommandTag.FLASH_READ_ONCE, 0, index, count) cmd_response = self._process_cmd(cmd_packet) return cmd_response.data if self._check_response(cmd_packet, cmd_response) else None def flash_program_once(self, index: int, data: bytes) -> bool: """ Write into MCU flash program once region (max 8 bytes) :param index: Start index :param data: Input data aligned to 4 or 8 bytes """ assert len(data) in (4, 8) logger.info(f"CMD: FlashProgramOnce(index={index}, data={data})") cmd_packet = CmdPacket(CommandTag.FLASH_PROGRAM_ONCE, 0, index, len(data), data=data) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def flash_read_resource(self, address: int, length: int, option: int = 1) -> Optional[bytes]: """ Read resource of flash module :param address: Start address :param length: Number of bytes :param option: """ logger.info(f"CMD: FlashReadResource(address=0x{address:08X}, length={length}, option={option})") cmd_packet = CmdPacket(CommandTag.FLASH_READ_RESOURCE, 0, address, length, option) cmd_response = self._process_cmd(cmd_packet) if self._check_response(cmd_packet, cmd_response, False): return self._read_data(CommandTag.FLASH_READ_RESOURCE, cmd_response.length) return None def configure_memory(self, address: int, mem_id: int) -> bool: """ Configure memory :param address: The address in memory where are locating configuration data :param mem_id: External memory ID """ logger.info(f"CMD: ConfigureMemory({ExtMemId[mem_id]}, address=0x{address:08X})") cmd_packet = CmdPacket(CommandTag.CONFIGURE_MEMORY, 0, mem_id, address) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def reliable_update(self, address: int) -> bool: """ Reliable Update :param address: """ logger.info(f"CMD: ReliableUpdate(address=0x{address:08X})") cmd_packet = CmdPacket(CommandTag.RELIABLE_UPDATE, 0, address) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def generate_key_blob(self, dek_data: bytes, count: int = 72) -> Optional[bytes]: """ Generate Key Blob :param dek_data: Data Encryption Key as bytes :param count: Key blob count (default: 72 - AES128bit) """ logger.info(f"CMD: GenerateKeyBlob(dek_len={len(dek_data)}, count={count})") cmd_packet = CmdPacket(CommandTag.GENERATE_KEY_BLOB, 1, 0, len(dek_data), 0) cmd_response = self._process_cmd(cmd_packet) if self._check_response(cmd_packet, cmd_response, False): return None if not self._send_data(CommandTag.GENERATE_KEY_BLOB, dek_data): return None cmd_packet = CmdPacket(CommandTag.GENERATE_KEY_BLOB, 0, 0, count, 1) cmd_response = self._process_cmd(cmd_packet) if self._check_response(cmd_packet, cmd_response, False): return self._read_data(CommandTag.GENERATE_KEY_BLOB, cmd_response.length) return None def kp_enroll(self) -> bool: """ Key provisioning: Enroll Command (start PUF) """ logger.info("CMD: [KeyProvisioning] Enroll") cmd_packet = CmdPacket(CommandTag.KEY_PROVISIONING, 0, KeyProvOperation.ENROLL) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def kp_set_intrinsic_key(self, key_type: int, key_size: int) -> bool: """ Key provisioning: Generate Intrinsic Key :param key_type: :param key_size: """ logger.info(f"CMD: [KeyProvisioning] SetIntrinsicKey(type={key_type}, key_size={key_size})") cmd_packet = CmdPacket(CommandTag.KEY_PROVISIONING, 0, KeyProvOperation.SET_INTRINSIC_KEY, key_type, key_size) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def kp_write_nonvolatile(self, mem_id: int = 0) -> bool: """ Key provisioning: Write the key to a nonvolatile memory :param mem_id: The memory ID (default: 0) """ logger.info(f"CMD: [KeyProvisioning] WriteNonVolatileMemory(mem_id={mem_id})") cmd_packet = CmdPacket(CommandTag.KEY_PROVISIONING, 0, KeyProvOperation.WRITE_NON_VOLATILE, mem_id) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def kp_read_nonvolatile(self, mem_id: int = 0) -> bool: """ Key provisioning: Load the key from a nonvolatile memory to bootloader :param mem_id: The memory ID (default: 0) """ logger.info(f"CMD: [KeyProvisioning] ReadNonVolatileMemory(mem_id={mem_id})") cmd_packet = CmdPacket(CommandTag.KEY_PROVISIONING, 0, KeyProvOperation.READ_NON_VOLATILE, mem_id) cmd_response = self._process_cmd(cmd_packet) return self._check_response(cmd_packet, cmd_response) def kp_set_user_key(self, key_type: int, key_data: bytes) -> bool: """ Key provisioning: Send the user key specified by to bootloader. :param key_type: :param key_data: """ logger.info(f"CMD: [KeyProvisioning] SetUserKey(key_type={key_type}, key_len={len(key_data)})") cmd_packet = CmdPacket(CommandTag.KEY_PROVISIONING, 1, KeyProvOperation.SET_USER_KEY, key_type, len(key_data)) cmd_response = self._process_cmd(cmd_packet) if self._check_response(cmd_packet, cmd_response, False): return self._send_data(CommandTag.KEY_PROVISIONING, key_data) return False def kp_write_key_store(self, key_type: int, key_data: bytes) -> bool: """ Key provisioning: Write key data into key store area. :param key_type: :param key_data: """ key_len = len(key_data) logger.info(f"CMD: [KeyProvisioning] WriteKeyStore(key_type={key_type}, key_len={key_len})") cmd_packet = CmdPacket(CommandTag.KEY_PROVISIONING, 1, KeyProvOperation.WRITE_KEY_STORE, key_type, key_len) cmd_response = self._process_cmd(cmd_packet) if self._check_response(cmd_packet, cmd_response, False): return self._send_data(CommandTag.KEY_PROVISIONING, key_data) return False def kp_read_key_store(self) -> Optional[bytes]: """ Key provisioning: Read key data from key store area. """ logger.info(f"CMD: [KeyProvisioning] ReadKeyStore") cmd_packet = CmdPacket(CommandTag.KEY_PROVISIONING, 0, KeyProvOperation.READ_KEY_STORE) cmd_response = self._process_cmd(cmd_packet) if self._check_response(cmd_packet, cmd_response, False): return self._read_data(CommandTag.KEY_PROVISIONING, cmd_response.length) return None ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1576530951.0 mboot-0.3.0/mboot/memories.py0000777000175000000000000000360500000000000016442 0ustar00martinroot00000000000000# Copyright (c) 2019 Martin Olejar # # SPDX-License-Identifier: BSD-3-Clause # The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution # or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText from easy_enum import Enum ######################################################################################################################## # McuBoot External Memory ID ######################################################################################################################## class ExtMemId(Enum): """ McuBoot External Memory Property Tags """ QUAD_SPI0 = (1, 'QSPI', 'Quad SPI Memory 0') IFR0 = (4, 'Nonvolatile information register 0 (only used by SB loader)') SEMC_NOR = (8, 'SEMC-NOR', 'SEMC NOR Memory') FLEX_SPI_NOR = (9, 'FLEX-SPI-NOR', 'Flex SPI NOR Memory') SPIFI_NOR = (10, 'SPIFI-NOR', 'SPIFI NOR Memory') FLASH_EXEC_ONLY = (16, 'FLASH-EXEC', 'Execute-Only region on internal Flash') SEMC_NAND = (256, 'SEMC-NAND', 'SEMC NAND Memory') SPI_NAND = (257, 'SPI-NAND', 'SPI NAND Memory') SPI_NOR_EEPROM = (272, 'SPI-MEM', 'SPI NOR/EEPROM Memory') I2C_NOR_EEPROM = (273, 'I2C-MEM', 'I2C NOR/EEPROM Memory') SD_CARD = (288, 'SD', 'eSD/SD/SDHC/SDXC Memory Card') MMC_CARD = (289, 'MMC', 'MMC/eMMC Memory Card') ######################################################################################################################## # McuBoot External Memory Property Tags ######################################################################################################################## class ExtMemPropTags(Enum): """ McuBoot External Memory Property Tags """ INIT_STATUS = 0x00000000 START_ADDRESS = 0x00000001 SIZE_IN_KBYTES = 0x00000002 PAGE_SIZE = 0x00000004 SECTOR_SIZE = 0x00000008 BLOCK_SIZE = 0x00000010 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578185059.0 mboot-0.3.0/mboot/properties.py0000777000175000000000000004547600000000000017032 0ustar00martinroot00000000000000# Copyright (c) 2019 Martin Olejar # # SPDX-License-Identifier: BSD-3-Clause # The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution # or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText from typing import Union, Any from easy_enum import Enum from .commands import CommandTag from .memories import ExtMemPropTags, ExtMemId from .errorcodes import StatusCode ######################################################################################################################## # McuBoot helper functions ######################################################################################################################## def size_fmt(value: Union[int, float], kibibyte: bool = True) -> str: """ Convert size value into string format :param value: The raw value :param kibibyte: True if 1024 Bytes represent 1kB or False if 1000 Bytes represent 1kB """ base, suffix = [(1000., 'B'), (1024., 'iB')][kibibyte] for x in ['B'] + [x + suffix for x in list('kMGTP')]: if -base < value < base: break value /= base return "{} {}".format(value, x) if x == 'B' else "{:3.1f} {}".format(value, x) ######################################################################################################################## # McuBoot helper classes ######################################################################################################################## class Version: """ McuBoot current and target version type """ __slots__ = ('mark', 'major', 'minor', 'fixation') def __init__(self, *args, **kwargs): self.mark = kwargs.get('mark', None) self.major = kwargs.get('major', 0) self.minor = kwargs.get('minor', 0) self.fixation = kwargs.get('fixation', 0) if args: if isinstance(args[0], int): self.from_int(args[0]) elif isinstance(args[0], str): self.from_str(args[0]) else: raise TypeError("Value must be 'str' or 'int' type !") def __eq__(self, obj): return isinstance(obj, Version) and self.mark == obj.mark and self.major == obj.major and \ self.minor == obj.minor and self.fixation == obj.fixation def __lt__(self, obj): return self.to_int(True) < obj.to_int(True) def __le__(self, obj): return self.to_int(True) <= obj.to_int(True) def __gt__(self, obj): return self.to_int(True) > obj.to_int(True) def __ge__(self, obj): return self.to_int(True) >= obj.to_int(True) def __str__(self): return self.to_str() def __repr__(self): return f"" def from_int(self, value: int): """ Parse version data from raw int value :param value: Raw integer input """ mark = (value >> 24) & 0xFF self.mark = chr(mark) if 64 < mark < 91 else None self.major = (value >> 16) & 0xFF self.minor = (value >> 8) & 0xFF self.fixation = value & 0xFF def from_str(self, value: str): """ Parse version data from string value :param value: String representation input """ mark_major, minor, fixation = value.split('.') if len(mark_major) > 1 and mark_major[0] not in "0123456789": self.mark = mark_major[0] self.major = int(mark_major[1:]) else: self.major = int(mark_major) self.minor = int(minor) self.fixation = int(fixation) def to_int(self, no_mark: bool = False) -> int: """ Get version value in raw integer format :param no_mark: If True, return value without mark """ value = self.major << 16 | self.minor << 8 | self.fixation return value if no_mark or self.mark is None else ord(self.mark) << 24 | value def to_str(self, no_mark: bool = False) -> str: """ Get version value in readable string format :param no_mark: If True, return value without mark """ value = f"{self.major}.{self.minor}.{self.fixation}" return value if no_mark or self.mark is None else self.mark + value ######################################################################################################################## # McuBoot Properties ######################################################################################################################## class PropertyTag(Enum): """ McuBoot Properties """ # LIST_PROPERTIES = (0x00, 'ListProperties', 'List Properties') CURRENT_VERSION = (0x01, 'CurrentVersion', 'Current Version') AVAILABLE_PERIPHERALS = (0x02, 'AvailablePeripherals', 'Available Peripherals') FLASH_START_ADDRESS = (0x03, 'FlashStartAddress', 'Flash Start Address') FLASH_SIZE = (0x04, 'FlashSize', 'Flash Size') FLASH_SECTOR_SIZE = (0x05, 'FlashSectorSize', 'Flash Sector Size') FLASH_BLOCK_COUNT = (0x06, 'FlashBlockCount', 'Flash Block Count') AVAILABLE_COMMANDS = (0x07, 'AvailableCommands', 'Available Commands') CRC_CHECK_STATUS = (0x08, 'CrcCheckStatus', 'CRC Check Status') LAST_ERROR = (0x09, 'LastError', 'Last Error Value') VERIFY_WRITES = (0x0A, 'VerifyWrites', 'Verify Writes') MAX_PACKET_SIZE = (0x0B, 'MaxPacketSize', 'Max Packet Size') RESERVED_REGIONS = (0x0C, 'ReservedRegions', 'Reserved Regions') VALIDATE_REGIONS = (0x0D, 'ValidateRegions', 'Validate Regions') RAM_START_ADDRESS = (0x0E, 'RamStartAddress', 'RAM Start Address') RAM_SIZE = (0x0F, 'RamSize', 'RAM Size') SYSTEM_DEVICE_IDENT = (0x10, 'SystemDeviceIdent', 'System Device Identification') FLASH_SECURITY_STATE = (0x11, 'FlashSecurityState', 'Flash Security State') UNIQUE_DEVICE_IDENT = (0x12, 'UniqueDeviceIdent', 'Unique Device Identification') FLASH_FAC_SUPPORT = (0x13, 'FlashFacSupport', 'Flash Fac. Support') FLASH_ACCESS_SEGMENT_SIZE = (0x14, 'FlashAccessSegmentSize', 'Flash Access Segment Size') FLASH_ACCESS_SEGMENT_COUNT = (0x15, 'FlashAccessSegmentCount', 'Flash Access Segment Count') FLASH_READ_MARGIN = (0x16, 'FlashReadMargin', 'Flash Read Margin') QSPI_INIT_STATUS = (0x17, 'QspiInitStatus', 'QuadSPI Initialization Status') TARGET_VERSION = (0x18, 'TargetVersion', 'Target Version') EXTERNAL_MEMORY_ATTRIBUTES = (0x19, 'ExternalMemoryAttributes', 'External Memory Attributes') RELIABLE_UPDATE_STATUS = (0x1A, 'ReliableUpdateStatus', 'Reliable Update Status') FLASH_PAGE_SIZE = (0x1B, 'FlashPageSize', 'Flash Page Size') IRQ_NOTIFIER_PIN = (0x1C, 'IrqNotifierPin', 'Irq Notifier Pin') PFR_KEYSTORE_UPDATE_OPT = (0x1D, 'PfrKeystoreUpdateOpt', 'PFR Keystore Update Opt') class PeripheryTag(Enum): UART = (0x01, 'UART', 'UART Interface') I2C_SLAVE = (0x02, 'I2C-Slave', 'I2C Slave Interface') SPI_SLAVE = (0x04, 'SPI-Slave', 'SPI Slave Interface') CAN = (0x08, 'CAN', 'CAN Interface') USB_HID = (0x10, 'USB-HID', 'USB HID-Class Interface') USB_CDC = (0x20, 'USB-CDC', 'USB CDC-Class Interface') USB_DFU = (0x40, 'USB-DFU', 'USB DFU-Class Interface') class FlashReadMargin(Enum): NORMAL = (0, 'Normal') USER = (1, 'User') FACTORY = (2, 'Factory') class PfrKeystoreUpdateOpt(Enum): KEY_PROVISIONING = (0, 'KeyProvisioning') WRITE_MEMORY = (1, 'WriteMemory') ######################################################################################################################## # McuBoot Properties Values ######################################################################################################################## class PropertyValueBase: """ Base class for property value """ __slots__ = ('tag', 'name', 'desc') def __init__(self, tag, **kwargs): self.tag = tag self.name = kwargs.get('name', PropertyTag.get(tag, '')) self.desc = kwargs.get('desc', PropertyTag.desc(tag)) def __str__(self): return f"{self.name} = {self.to_str()}" def to_str(self): raise NotImplementedError() class IntValue(PropertyValueBase): """ Property integer value class """ __slots__ = ('value', '_fmt',) def __init__(self, tag, raw_values, **kwargs): super().__init__(tag, **kwargs) self._fmt = kwargs.get('str_format', 'dec') self.value = raw_values[0] def to_int(self): return self.value def to_str(self): if self._fmt == 'size': str_value = size_fmt(self.value) elif self._fmt == 'hex': str_value = f"0x{self.value:08X}" elif self._fmt == 'dec': str_value = str(self.value) else: str_value = self._fmt.format(self.value) return str_value class BoolValue(PropertyValueBase): """ Property bool value class """ __slots__ = ('value', '_true_values', '_false_values', '_true_string', '_false_string') def __init__(self, tag, raw_values, **kwargs): super().__init__(tag, **kwargs) self._true_values = kwargs.get('true_values', (1,)) self._true_string = kwargs.get('true_string', 'YES') self._false_values = kwargs.get('false_values', (0,)) self._false_string = kwargs.get('false_string', 'NO') self.value = raw_values[0] def __bool__(self): return self.value in self._true_values def to_int(self): return self.value def to_str(self): return self._true_string if self.value in self._true_values else self._false_string class EnumValue(PropertyValueBase): __slots__ = ('value', 'enum', '_na_msg') def __init__(self, tag, raw_values, **kwargs): super().__init__(tag, **kwargs) self._na_msg = kwargs.get('na_msg', 'Unknown Item') self.enum = kwargs['enum'] self.value = raw_values[0] def to_int(self): return self.value def to_str(self): return self.enum[self.value] if self.value in self.enum else f"{self._na_msg}: {self.value}" class VersionValue(PropertyValueBase): __slots__ = ('value',) def __init__(self, tag, raw_values, **kwargs): super().__init__(tag, **kwargs) self.value = Version(raw_values[0]) def to_int(self): return self.value.to_int() def to_str(self): return self.value.to_str() class DeviceUidValue(PropertyValueBase): __slots__ = ('value', '_count') def __init__(self, tag, raw_values, **kwargs): super().__init__(tag, **kwargs) self._count = len(raw_values) self.value = 0 for i, v in enumerate(raw_values): self.value |= v << (i * 32) def to_int(self): return self.value def to_str(self): fmt = f"{{:0{self._count * 8}X}}" return fmt.format(self.value) class ReservedRegionsValue(PropertyValueBase): __slots__ = ('regions',) def __init__(self, tag, raw_values, **kwargs): super().__init__(tag, **kwargs) self.regions = [] for i in range(0, len(raw_values), 2): start = raw_values[i] end = raw_values[i + 1] if start == end: continue self.regions.append((start, end)) def to_str(self): return [f"0x{r[0]:08X} - 0x{r[1]:08X}, {size_fmt(r[1] - r[0])}" for r in self.regions] class AvailablePeripheralsValue(PropertyValueBase): __slots__ = ('value',) def __init__(self, tag, raw_values, **kwargs): super().__init__(tag, **kwargs) self.value = raw_values[0] def to_int(self): return self.value def to_str(self): return [key for key, value, _ in PeripheryTag if value & self.value] class AvailableCommandsValue(PropertyValueBase): __slots__ = ('value',) @property def tags(self): return [tag_value for _, tag_value, _ in CommandTag if (1 << tag_value) & self.value] def __init__(self, tag, raw_values, **kwargs): super().__init__(tag, **kwargs) self.value = raw_values[0] def __contains__(self, item): return isinstance(item, int) and (1 << item) & self.value def to_str(self): return [name for name, value, _ in CommandTag if (1 << value) & self.value] class IrqNotifierPinValue(PropertyValueBase): __slots__ = ('value',) @property def pin(self): return self.value & 0xFF @property def port(self): return (self.value >> 8) & 0xFF @property def enabled(self): return self.value & (1 << 32) def __init__(self, tag, raw_values, **kwargs): super().__init__(tag, **kwargs) self.value = raw_values[0] def __bool__(self): return self.enabled def to_str(self): return f"IRQ Port[{self.port}], Pin[{self.pin}] is {'enabled' if self.enabled else 'disabled'}" class ExternalMemoryAttributesValue(PropertyValueBase): __slots__ = ('value', 'mem_id', 'start_address', 'total_size', 'page_size', 'sector_size', 'block_size') def __init__(self, tag, raw_values, **kwargs): super().__init__(tag, **kwargs) self.mem_id = kwargs.get('mem_id', 0) self.start_address = raw_values[1] if raw_values[0] & ExtMemPropTags.START_ADDRESS else None self.total_size = raw_values[2] * 1024 if raw_values[0] & ExtMemPropTags.SIZE_IN_KBYTES else None self.page_size = raw_values[3] if raw_values[0] & ExtMemPropTags.PAGE_SIZE else None self.sector_size = raw_values[4] if raw_values[0] & ExtMemPropTags.SECTOR_SIZE else None self.block_size = raw_values[5] if raw_values[0] & ExtMemPropTags.BLOCK_SIZE else None self.value = raw_values[0] def to_str(self): str_values = [] if self.start_address is not None: str_values.append(f"Start Address: 0x{self.start_address:08X}") if self.total_size is not None: str_values.append(f"Total Size: {size_fmt(self.total_size)}") if self.page_size is not None: str_values.append(f"Page Size: {size_fmt(self.page_size)}") if self.sector_size is not None: str_values.append(f"Sector Size: {size_fmt(self.sector_size)}") if self.block_size is not None: str_values.append(f"Block Size: {size_fmt(self.block_size)}") return str_values ######################################################################################################################## # McuBoot property response parser ######################################################################################################################## PROPERTIES = { PropertyTag.CURRENT_VERSION: { 'class': VersionValue, 'kwargs': {}}, PropertyTag.AVAILABLE_PERIPHERALS: { 'class': AvailablePeripheralsValue, 'kwargs': {}}, PropertyTag.FLASH_START_ADDRESS: { 'class': IntValue, 'kwargs': {'str_format': 'hex'}}, PropertyTag.FLASH_SIZE: { 'class': IntValue, 'kwargs': {'str_format': 'size'}}, PropertyTag.FLASH_SECTOR_SIZE: { 'class': IntValue, 'kwargs': {'str_format': 'size'}}, PropertyTag.FLASH_BLOCK_COUNT: { 'class': IntValue, 'kwargs': {'str_format': 'dec'}}, PropertyTag.AVAILABLE_COMMANDS: { 'class': AvailableCommandsValue, 'kwargs': {}}, PropertyTag.CRC_CHECK_STATUS: { 'class': IntValue, 'kwargs': {'str_format': 'hex'}}, PropertyTag.VERIFY_WRITES: { 'class': BoolValue, 'kwargs': {'true_string': 'ON', 'false_string': 'OFF'}}, PropertyTag.LAST_ERROR: { 'class': EnumValue, 'kwargs': {'enum': StatusCode, 'na_msg': 'Unknown Error'}}, PropertyTag.MAX_PACKET_SIZE: { 'class': IntValue, 'kwargs': {'str_format': 'size'}}, PropertyTag.RESERVED_REGIONS: { 'class': ReservedRegionsValue, 'kwargs': {}}, PropertyTag.VALIDATE_REGIONS: { 'class': BoolValue, 'kwargs': {'true_string': 'ON', 'false_string': 'OFF'}}, PropertyTag.RAM_START_ADDRESS: { 'class': IntValue, 'kwargs': {'str_format': 'hex'}}, PropertyTag.RAM_SIZE: { 'class': IntValue, 'kwargs': {'str_format': 'size'}}, PropertyTag.SYSTEM_DEVICE_IDENT: { 'class': IntValue, 'kwargs': {'str_format': 'hex'}}, PropertyTag.FLASH_SECURITY_STATE: { 'class': BoolValue, 'kwargs': {'true_values': (0x00000000, 0x5AA55AA5), 'true_string': 'Unlocked', 'false_values': (0x00000001, 0xC33CC33C), 'false_string': 'Locked'}}, PropertyTag.UNIQUE_DEVICE_IDENT: { 'class': DeviceUidValue, 'kwargs': {}}, PropertyTag.FLASH_FAC_SUPPORT: { 'class': BoolValue, 'kwargs': {'true_string': 'ON', 'false_string': 'OFF'}}, PropertyTag.FLASH_ACCESS_SEGMENT_SIZE: { 'class': IntValue, 'kwargs': {'str_format': 'size'}}, PropertyTag.FLASH_ACCESS_SEGMENT_COUNT: { 'class': IntValue, 'kwargs': {'str_format': 'dec'}}, PropertyTag.FLASH_READ_MARGIN: { 'class': EnumValue, 'kwargs': {'enum': FlashReadMargin, 'na_msg': 'Unknown Margin'}}, PropertyTag.QSPI_INIT_STATUS: { 'class': EnumValue, 'kwargs': {'enum': StatusCode, 'na_msg': 'Unknown Error'}}, PropertyTag.TARGET_VERSION: { 'class': VersionValue, 'kwargs': {}}, PropertyTag.EXTERNAL_MEMORY_ATTRIBUTES: { 'class': ExternalMemoryAttributesValue, 'kwargs': {}}, PropertyTag.RELIABLE_UPDATE_STATUS: { 'class': EnumValue, 'kwargs': {'enum': StatusCode, 'na_msg': 'Unknown Error'}}, PropertyTag.FLASH_PAGE_SIZE: { 'class': IntValue, 'kwargs': {'str_format': 'size'}}, PropertyTag.IRQ_NOTIFIER_PIN: { 'class': IrqNotifierPinValue, 'kwargs': {}}, PropertyTag.PFR_KEYSTORE_UPDATE_OPT: { 'class': EnumValue, 'kwargs': {'enum': PfrKeystoreUpdateOpt, 'na_msg': 'Unknown'}}, } def parse_property_value(prop_tag: int, raw_values: list, mem_id: int = 0) -> Any: """ Parse property raw values :param prop_tag: The property tag, see 'PropertyTag' enum :param raw_values: The property values :param mem_id: External memory ID (default: 0) """ if prop_tag not in PROPERTIES.keys(): return None cls = PROPERTIES[prop_tag]['class'] # type: ignore kwargs = PROPERTIES[prop_tag]['kwargs'] # type: ignore kwargs['mem_id'] = mem_id # type: ignore return cls(prop_tag, raw_values, **kwargs) # type: ignore ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1578341034.9904559 mboot-0.3.0/mboot.egg-info/0000777000175000000000000000000000000000000015733 5ustar00martinroot00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578341034.0 mboot-0.3.0/mboot.egg-info/PKG-INFO0000777000175000000000000004106400000000000017040 0ustar00martinroot00000000000000Metadata-Version: 2.1 Name: mboot Version: 0.3.0 Summary: Python module for communication with NXP MCU Bootloader Home-page: https://github.com/molejar/pyMBoot Author: Martin Olejar Author-email: martin.olejar@gmail.com License: BSD3 Description: pyMBoot ======= [![Build Status](https://travis-ci.org/molejar/pyMBoot.svg?branch=master)](https://travis-ci.org/molejar/pyMBoot) [![PyPI Status](https://img.shields.io/pypi/v/mboot.svg)](https://pypi.python.org/pypi/mboot) [![Python Version](https://img.shields.io/pypi/pyversions/mboot.svg)](https://www.python.org) pyMBoot is an Open Source python based library for configuring and upgrading the firmware in NXP Microcontrolers via embedded [MCUBOOT](https://www.nxp.com/support/developer-resources/software-development-tools/mcuxpresso-software-and-tools/mcuboot-mcu-bootloader-for-nxp-microcontrollers:MCUBOOT) (MCU Bootloader). Detailed description of `MCUBOOT / KBOOT` key features and functionality is located [here](https://freescale.jiveon.com/docs/DOC-104512). > This project is still in developing phase. Please, test it and report founded issues. Dependencies ------------ - [Python >3.6](https://www.python.org) - The interpreter for Python programing language - [Click](http://click.pocoo.org) - Python package for creating beautiful command line interface. - [bincopy](https://github.com/eerimoq/bincopy) - Python package for parsing S-Record, Intel HEX and TI-TXT files. - [easy_enum](https://github.com/molejar/pyEnum) - User friendly implementation of documented Enum type for Python language. - [PyUSB](https://walac.github.io/pyusb/) - Python package to access USB devices in Linux OS. - [PyWinUSB](https://github.com/rene-aguirre/pywinusb) - Python package that simplifies USB-HID communications on Windows OS. - [pyserial](https://github.com/pyserial/pyserial) - Python package for communication over Serial port in Linux and Windows OS. Installation ------------ ```bash $ pip install mboot ``` To install the latest version from master branch execute in shell following command: ```bash $ pip install -U https://github.com/molejar/pyMBoot/archive/master.zip ``` In case of development, install it from cloned sources: ```bash $ git clone https://github.com/molejar/pyMBoot.git $ cd pyMBoot $ pip install -U -e . ``` **NOTE:** You may run into a permissions issues running these commands. Here are a few options how to fix it: 1. Run with `sudo` to install pyMBoot and dependencies globally 2. Specify the `--user` option to install locally into your home directory (export "~/.local/bin" into PATH variable if haven't). 3. Run the command in a [virtualenv](https://virtualenv.pypa.io/en/latest/) local to a specific project working set. > For running `mboot` module or CLI without root privileges in Linux OS copy following udev rules [90-imx-sdp.rules](https://github.com/molejar/pyIMX/blob/master/udev/90-imx-sdp.rules) into `/etc/udev/rules.d` directory and reload it with command: `sudo udevadm control --reload-rules`. Usage ----- The API of `mboot` module is intuitive and fully reflecting the functionality described in reference manual of any supported device. It's basic usage is presented in following example. ```python import mboot devices = mboot.scan_usb() if devices: mb = mboot.McuBoot(devices[0]) mb.open() # read 100 bytes from address 0 data = mb.read_memory(0, 100) if data is None: print(mb.status_info) mb.close() exit() # other commands ... mb.close() ``` `McuBoot` class is supporting `with` statement what is eliminating the explicit call of `open` and `close` methods. The code then looks more cleaner as you can see in following example. ```python from mboot import scan_usb, McuBoot devices = scan_usb() if devices: with McuBoot(devices[0]) as mb: # read 100 bytes from address 0 data = mb.read_memory(0, 100) if data is None: print(mb.status_info) exit() # other commands ... ``` > If you call `reset()` command inside `with` block, the device is automatically reopened. You can skip this with explicit argument `reset(reopen=False)` By default is command error propagated by return value and must be processed individually for every command. In many use-cases is code execution interrupted if any command finish with error. Therefore you have the option to enable the exception also for command error. The code is then much more readable as you can see in flowing example. ```python from mboot import scan_usb, McuBoot, McuBootError devices = scan_usb() if devices: try: with McuBoot(devices[0], True) as mb: # read 100 bytes from address 0 data = mb.read_memory(0, 100) # other commands ... except McuBootError as e: print(str(e)) ``` `mboot` module is implementing also logging functionality for easy debugging all communication interfaces. To get it working you need only import `logging` module and set the logging level (`DEBUG` or `INFO`) with following line of code: `logging.basicConfig(level=logging.DEBUG)` ```python import logging logging.basicConfig(level=logging.DEBUG) ``` **The example of terminal output with enabled logging functionality:** ```text INFO:MBOOT:Connect: USB COMPOSITE DEVICE (0x15A2, 0x0073) DEBUG:MBOOT:USB:Open Interface INFO:MBOOT:CMD: ReadMemory(address=0x00000000, length=100, mem_id=0) DEBUG:MBOOT:TX-PACKET: Tag=ReadMemory, Flags=0x00, p0=0x00000000, p1=0x00000064, p2=0x00000000 DEBUG:MBOOT:USB:OUT[64]: 01 00 20 00 03 00 00 03 00 00 00 00 64 00 00 00 00 00 00 00 00 00 00 00 00 ... DEBUG:MBOOT:USB:IN [36]: 03 00 0C 00 A3 01 00 02 00 00 00 00 64 00 00 00 00 00 00 00 00 00 00 00 00 ... INFO:MBOOT:RX-PACKET: Tag=ReadMemoryResponse, Status=Success, Length=100 DEBUG:MBOOT:USB:IN [36]: 04 00 20 00 00 60 00 20 C1 00 00 00 0D 85 00 00 09 01 00 00 00 00 00 00 00 ... DEBUG:MBOOT:USB:IN [36]: 04 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 09 01 00 00 00 00 00 00 00 ... DEBUG:MBOOT:USB:IN [36]: 04 00 20 00 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 09 ... DEBUG:MBOOT:USB:IN [36]: 04 00 04 00 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 09 ... DEBUG:MBOOT:USB:IN [36]: 03 00 0C 00 A0 00 00 02 00 00 00 00 03 00 00 00 09 01 00 00 09 01 00 00 09 ... DEBUG:MBOOT:RX-PACKET: Tag=GenericResponse, Status=Success, Cmd=ReadMemory INFO:MBOOT:CMD: Successfully Received 100 from 100 Bytes DEBUG:MBOOT:USB:Close Interface ``` [ mboot ] Tool -------------- The `mboot` module is distributed with command-line utility, which demonstrate the complete functionality of this library and can be used as replacement of `blhos` tool. If you write `mboot` into shell and click enter, then you get the description of its usage. For getting the help of individual commands just use `mboot -?`. ``` bash $ mboot --help Usage: mboot [OPTIONS] COMMAND [ARGS]... NXP MCU Bootloader Command Line Interface, version: 0.3.0 NOTE: Development version, be carefully with it usage ! Options: -t, --target TEXT Select target MKL27, LPC55, ... [optional] -d, --debug INTEGER RANGE Debug level: 0-off, 1-info, 2-debug -v, --version Show the version and exit. -?, --help Show this message and exit. Commands: call Call code from specified address efuse Read/Write eFuse from MCU erase Erase MCU internal or external memory execute Execute code from specified address fill Fill MCU memory with specified pattern info Get MCU info (mboot properties) keyblob Generate the Blob for given DEK Key kp-enroll Key provisioning: Enroll kp-gen-key Key provisioning: Generate Intrinsic Key kp-read-kstore Key provisioning: Read the key from key store area kp-read-nvm Key provisioning: Read the key from nonvolatile memory kp-user-key Key provisioning: Send the user key to a bootloader kp-write-kstore Key provisioning: Write the key into key store area kp-write-nvm Key provisioning: Write the key into nonvolatile memory mconf Configure external memory mlist Get list of available memories otp Read/Write internal OTP segment read Read data from MCU internal or external memory reset Reset MCU resource Flash read resource sbfile Receive SB file unlock Unlock MCU update Copy backup app from address to main app region write Write data into MCU internal or external memory ``` > If USB device is not in known devices list, then use `-t or --target` argument and directly specify the device VID:PID. Example: **-t 0x15A2:0x0073**
#### $ mboot info Read bootloader properties from connected MCU. ```bash $ mboot info DEVICE: Kinetis Bootloader (0x15A2, 0x0073) CurrentVersion: K1.0.0 AvailablePeripherals: - UART - I2C-Slave - SPI-Slave - USB-HID FlashStartAddress: 0x00000000 FlashSize: 256.0 kiB FlashSectorSize: 1.0 kiB FlashBlockCount: 2 AvailableCommands: - FlashEraseAll - FlashEraseRegion - ReadMemory - FillMemory - FlashSecurityDisable - ReceiveSBFile - Call - Reset - SetProperty VerifyWrites: ON MaxPacketSize: 32 B ReservedRegions: - 0x1FFFF800 - 0x20000687, 3.6 kiB ValidateRegions: ON RamStartAddress: 0x1FFFE000 RamSize: 32.0 kiB SystemDeviceIdent: 0x23160D82 FlashSecurityState: Unlocked ```
#### $ mboot mlist Get list of available memories (internal and external) ```bash $ mboot info DEVICE: Kinetis Bootloader (0x15A2, 0x0073) Internal Flash: 0) 0x00000000 - 0x00040000, Size: 256.0 kiB, Sector Size: 1.0 kiB Internal Ram: 0) 0x1FFFE000 - 0x20006000, Size: 32.0 kiB ```
#### $ mboot read [OPTIONS] ADDRESS [LENGTH] Read data from MCU memory and store it into file as binary (*.bin), intel-hex (*.hex, *.ihex) or s-record (*.srec, *.s19) format. If output file is not specified, the data are dumped into stdout in readable format. > LENGTH argument is optional and as default will be used the size to end of memory ##### options: * **-c, --compress** - Compress dump output. (default: False) * **-f, --file** - Output file name with extension: *.bin, *.hex, *.ihex, *.srec or *.s19 * **-?, --help** - Show help message and exit. ``` bash $ mboot read 0 200 Reading from MCU memory, please wait ! ADDRESS | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F | 0123456789ABCDEF ----------------------------------------------------------------------------- 00000000 | 00 60 00 20 C1 00 00 00 D9 08 00 00 09 01 00 00 | .`. ............ 00000010 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ 00000020 | 00 00 00 00 00 00 00 00 00 00 00 00 09 01 00 00 | ................ 00000030 | 00 00 00 00 00 00 00 00 09 01 00 00 09 01 00 00 | ................ 00000040 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000050 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000060 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000070 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000080 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 00000090 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 000000A0 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 000000B0 | 09 01 00 00 09 01 00 00 09 01 00 00 09 01 00 00 | ................ 000000C0 | 0A 49 0B 4A 0B 4B 9B 1A | .I.J.K.. ----------------------------------------------------------------------------- ```
#### $ mboot write [OPTIONS] FILE Write data from attached FILE into MCU memory. ##### options: * **-a, --address** - Start Address. (default: 0) * **-o, --offset** - Offset of input data. (default: 0) * **-?, --help** - Show help message and exit. ``` bash $ mboot write blink.srec Wrote Successfully. ```
#### $ mboot erase [OPTIONS] Erase MCU memory from specified address and length or complete chip. ##### options: * **-m, --mass** - Erase complete MCU memory. * **-a, --address** - Start Address. * **-l, --length** - Count of bytes aligned to flash block size. * **-?, --help** - Show help message and exit. ``` bash $ mboot erase -m Chip Erased Successfully. ```
#### $ mboot unlock [OPTIONS] Unlock MCU memory. ##### options: * **-k, --key** - Use backdoor key as ASCII = S:123...8 or HEX = X:010203...08 * **-?, --help** - Show help message and exit. ``` bash $ mboot unlock Chip Unlocked Successfully. ```
#### $ mboot fill [OPTIONS] ADDRESS LENGTH Fill MCU memory with specified pattern ##### options: * **-p, --pattern** - Pattern format (default: 0xFFFFFFFF). * **-?, --help** - Show help message and exit. ``` bash $ mboot fill -p 0x11111111 0x1FFFE000 10 Filled Successfully. ```
#### $ mboot reset MCU SW reset ``` bash $ mboot reset ``` TODO ---- - Implement support for UART interface Platform: UNKNOWN Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX :: Linux Classifier: Environment :: Console Classifier: Topic :: Scientific/Engineering Classifier: Topic :: Software Development :: Embedded Systems Classifier: Topic :: Utilities Requires-Python: >=3.6 Description-Content-Type: text/markdown ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578341034.0 mboot-0.3.0/mboot.egg-info/SOURCES.txt0000777000175000000000000000067600000000000017633 0ustar00martinroot00000000000000README.md setup.py mboot/__init__.py mboot/__main__.py mboot/commands.py mboot/errorcodes.py mboot/exceptions.py mboot/mcuboot.py mboot/memories.py mboot/properties.py mboot.egg-info/PKG-INFO mboot.egg-info/SOURCES.txt mboot.egg-info/dependency_links.txt mboot.egg-info/entry_points.txt mboot.egg-info/requires.txt mboot.egg-info/top_level.txt mboot/connection/__init__.py mboot/connection/base.py mboot/connection/uart.py mboot/connection/usb.py././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578341034.0 mboot-0.3.0/mboot.egg-info/dependency_links.txt0000777000175000000000000000000100000000000022004 0ustar00martinroot00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578341034.0 mboot-0.3.0/mboot.egg-info/entry_points.txt0000777000175000000000000000005700000000000021236 0ustar00martinroot00000000000000[console_scripts] mboot = mboot.__main__:main ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578341034.0 mboot-0.3.0/mboot.egg-info/requires.txt0000777000175000000000000000017000000000000020334 0ustar00martinroot00000000000000click==7.0 pyserial==3.4 bincopy==16.0.0 easy_enum==0.3.0 pyusb==1.0.2 [:platform_system == "Windows"] pywinusb==0.4.2 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578341034.0 mboot-0.3.0/mboot.egg-info/top_level.txt0000777000175000000000000000000600000000000020464 0ustar00martinroot00000000000000mboot ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1578341034.993482 mboot-0.3.0/setup.cfg0000777000175000000000000000004600000000000014745 0ustar00martinroot00000000000000[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1578337995.0 mboot-0.3.0/setup.py0000777000175000000000000000344700000000000014646 0ustar00martinroot00000000000000#!/usr/bin/env python # Copyright (c) 2019 Martin Olejar # # SPDX-License-Identifier: BSD-3-Clause # The BSD-3-Clause license for this file can be found in the LICENSE file included with this distribution # or at https://spdx.org/licenses/BSD-3-Clause.html#licenseText from os import path from setuptools import setup, find_packages from mboot import __version__, __license__, __author__, __contact__ def get_long_description(): with open(path.join(path.dirname(path.abspath(__file__)), 'README.md'), encoding='utf8') as fp: return fp.read() setup( name='mboot', version=__version__, license=__license__, author=__author__, author_email=__contact__, url="https://github.com/molejar/pyMBoot", description='Python module for communication with NXP MCU Bootloader', long_description=get_long_description(), long_description_content_type='text/markdown', python_requires='>=3.6', setup_requires=[ 'setuptools>=40.0' ], install_requires=[ 'click==7.0', 'pyserial==3.4', 'bincopy==16.0.0', 'easy_enum==0.3.0', 'pyusb==1.0.2', 'pywinusb==0.4.2;platform_system=="Windows"', ], classifiers=[ 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'License :: OSI Approved :: BSD License', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX :: Linux', 'Environment :: Console', 'Topic :: Scientific/Engineering', 'Topic :: Software Development :: Embedded Systems', 'Topic :: Utilities', ], packages=find_packages('.'), entry_points={ 'console_scripts': [ 'mboot = mboot.__main__:main', ], } )