diff --git a/README.md b/README.md index 8ef4c0f..63e33f8 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,56 @@ -efm8load.py -=== +# efm8load.py -A python-only implementation of the efm8 bootloader protocol +A python-only implementation of the efm8 bootloader protocol. +Check out http://fishpepper.de/2016/10/15/efm8-bootloader-flash-tool-efm8load-py/ for more info. -see http://fishpepper.de/2016/10/15/efm8-bootloader-flash-tool-efm8load-py/ for more info -Status: -- tested on EFM8BB10F8G: identify, write, verify and read are working +## Status +| Microcontroller Unit (MCU) | Identify | Read | Write | +| - | - | - | - | +| EFM8BB10F8G | 🟢 | 🟢 | 🟢 | +| EFM8BB52F32G | 🟢 | 🟢 | 🟡 | -Usage: -- connect rs232 (3.3V level!) to the RX and TX pins of your MCU -- empty targets boot right into the bootloader (flash[0] = 0xFF -> bootloader) -- flashed targets need the C2D pin pulled low during powerup to enter bootloader mode +🟢 = Working, +🟡 = Not tested, +🔴 = Not working -TODO: -add a protection to stop users from overwriting the bootloader area + +## Info +- Connect the MCU to the rs232: + * MCU RXD to TXD + * MCU TXD to RXD + * MCU 3v3 to 3v3 **or** MCU 5v to 5v **(⚠️Do not mix 5v with 3v3! Check the datasheet for VVDD and `Voltage on VDD supply pin`⚠️)** + * MCU GND to GND + * MCU C2D to GND +- Empty targets boot right into the bootloader (flash[0] = 0xFF -> bootloader) + + +## Requirements +- python3 + pip +- USB to UART Bridge Controller (e.g. CP2102) +- CP210x USB to UART Bridge VCP Drivers +- C2D Pin **must** be pulled low (connected to GND) during powerup to enter the bootload mode (Review section `5.3.1 Entering Bootload Mode` [documents/an945-efm8-factory-bootloader-user-guide.pdf](documents/an945-efm8-factory-bootloader-user-guide.pdf) for the right Pin numbers) + + +## Setup +```bash +# Install the necessary packages +pip install -r requirements.txt +``` + +## Usage +```bash +# Identify the chip +python efm8load.py -p -i + +# Dump the flash memory to file +python efm8load.py -p -r dumped_flash.hex + +# ⚠️Upload/Write file to flash memory⚠️ +python efm8load.py -p -w dumped_flash.hex +``` + + +## TODO +- Add a protection to stop users from overwriting the bootloader area +- Information about decompiling the dumped flash memory diff --git a/documents/an945-efm8-factory-bootloader-user-guide.pdf b/documents/an945-efm8-factory-bootloader-user-guide.pdf new file mode 100644 index 0000000..f5ee639 Binary files /dev/null and b/documents/an945-efm8-factory-bootloader-user-guide.pdf differ diff --git a/dumps/.gitignore b/dumps/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/dumps/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/efm8load.py b/efm8load.py index 3efbd9d..3bda760 100755 --- a/efm8load.py +++ b/efm8load.py @@ -21,13 +21,12 @@ import crcmod import serial +from tqdm import tqdm +from collections import Counter from intelhex import IntelHex - -# make sure to install the python3 modules for serial, crcmod, and pip3: +# make sure to install the python modules for pyserial, tqdm, crcmod, intelhex: # sudo apt intstall python3-crcmod python3-serial python3-pip -# if you are missing the intelhex package, you can install it afterwars by -# pip3 install intelhex --user class COMMAND: IDENTIFY = 0x30 @@ -81,6 +80,9 @@ class EFM8Loader: }], 0x34 : ["EFM8BB3", { 0x01: ["EFM8BB31F64G-QFN32" , 64*1024, 512, 512], + }], + 0x39 : ["EFM8BB5", { + 0x08: ["EFM8BB52F32G-QFN32" , 15*2048, 2048, 2048], }] } @@ -271,30 +273,41 @@ def download(self, filename): #the bootloader protocol does not allow reading flash #however it allows to verify written bytes #we will exploit this feature to dump the flash contents - #for now assume 8kb flash - flash_size = 8 * 1024 + ih = IntelHex() - for address in range(flash_size): - #test one byte by byte - #first check 0x00 - byte = 0 - if (self.verify(address, [byte]) == RESPONSE.ACK): - ih[address] = byte - else: - #now start with 0xFF (empty flash) - for byte in range(0xFF, -1, -1): - if (self.verify(address, [byte]) == RESPONSE.ACK): - #success, the flash content on this address euals - ih[address] = byte - break - print("\r> flash[0x%04X] = 0x%02X" % (address, byte), end="") - sys.stdout.flush() - print("\n> finished") + # generate list with all possible hex adress values + byte_list = Counter() + for byte in range(0xFF, -1, -1): + byte_list[byte] += 1 + + + for address in tqdm( + range(self.flash_size), + desc="Dumping Flash", + unit=" Bytes", + ascii=" #", + smoothing=0.01, + dynamic_ncols=True, + unit_scale=True, + ): + sorted_byte_list = byte_list.most_common() + for curr_byte in sorted_byte_list: + byte=curr_byte[0] + if (self.verify(address, [byte]) == RESPONSE.ACK): + #success, the flash content on this address equals + ih[address] = byte + byte_list[byte] += 1 + break + #print(" flash[0x%04X] = 0x%02X" % (address, byte)) + print("\n> finished dumping") - #done, all flash contents have been read, now store this to the file - ih.write_hex_file(filename) + #done, all flash contents have been read, now store this to the files + ih.tofile("dumps/" + filename, format='hex') + ih.tofile("dumps/" + filename + ".bin", format='bin') + print("\n> dump written to disk successfully") + print("\n> done") def upload(self, filename): print("> uploading file '%s'" % (filename)) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..dc2fdea --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +crcmod==1.7 +intelhex==2.3.0 +pyserial==3.5 +tqdm==4.66.4