Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 51 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -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 V<sub>VDD</sub> 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 <COM_PORT> -i

# Dump the flash memory to file
python efm8load.py -p <COM_PORT> -r dumped_flash.hex

# ⚠️Upload/Write file to flash memory⚠️
python efm8load.py -p <COM_PORT> -w dumped_flash.hex
```


## TODO
- Add a protection to stop users from overwriting the bootloader area
- Information about decompiling the dumped flash memory
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would drop the line about decompiling, its not the scope of this tool

Binary file not shown.
2 changes: 2 additions & 0 deletions dumps/.gitignore
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would drop this, add the dumps directory to the toplevel gitignore instead
(or more preferably: drop prepending dumps/ in the file writer, see comment below)

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
61 changes: 37 additions & 24 deletions efm8load.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -81,6 +80,9 @@ class EFM8Loader:
}],
0x34 : ["EFM8BB3", {
0x01: ["EFM8BB31F64G-QFN32" , 64*1024, 512, 512],
}],
0x39 : ["EFM8BB5", {
0x08: ["EFM8BB52F32G-QFN32" , 15*2048, 2048, 2048],
}]
}

Expand Down Expand Up @@ -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 <byte>
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()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the sorting good for? speedup if there are many equal values?

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 <byte>
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')
Comment on lines +306 to +307
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would not prepend dumps here, you can always pass dumps/file.hex as commandline param. e.g. i use it to dump to /tmp/dump.hex
adding bin is nice :)


print("\n> dump written to disk successfully")
print("\n> done")

def upload(self, filename):
print("> uploading file '%s'" % (filename))
Expand Down
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
crcmod==1.7
intelhex==2.3.0
pyserial==3.5
tqdm==4.66.4