📝 2 Feb 2022
Pine64 PineDio Stack BL604 RISC-V Board
Something special happens when we flash firmware to BL602 and BL604 RISC-V boards…
It starts a tiny program inside the board to make flashing possible: The EFlash Loader.
Step by step we shall uncover what’s inside EFlash Loader, thanks to Ghidra the popular tool for Software Reverse Engineering.
Why are we doing this?
EFlash Loader is a critical part of the Flashing Process
(Good to understand how it works)
No Source Code is available for EFlash Loader
EFlash Loader is small (37 KB) and self-contained
EFlash Loader gets updated occasionally, so it’s good for us to see what’s changed
This is my first time using Ghidra so this might be a fun and educational exercise!
(But please bear with my ignorance 🙏)
Pine64 PineCone BL602 RISC-V Board
How does EFlash Loader flash firmware to BL602?
Here’s what happens when we run a Firmware Flasher on our computer to flash BL602…
Firmware Flasher sends the EFlash Loader executable to BL602
(Via USB UART, in 4 KB chunks)
BL602 receives and starts the EFlash Loader
(Assuming BL602 is in Flashing Mode)
Firmware Flasher sends the Flashing Image to EFlash Loader
(In 8 KB chunks)
EFlash Loader writes the Flashing Image to BL602’s Embedded Flash
Firmware Flasher verifies with EFlash Loader that the Flashing Image was written correctly
(With SHA256 hashing)
Flashing firmware to BL602 with blflash looks like this…
(First 20 seconds)
$ blflash flash nuttx.bin \
--port /dev/ttyUSB0
Start connection...
5ms send count 55
handshake sent elapsed 252µs
Connection Succeed
Sending eflash_loader...
Finished 2s 11KiB/s
5ms send count 500
handshake sent elapsed 5ms
Entered eflash_loader
Erase flash addr: 10000 size: 346432
Program flash...
Program done 4s 82KiB/s
Success
We see that blflash sends the EFlash Loader to BL602, followed by the Flashing Image.
(Which gets written to BL602’s Embedded Flash by EFlash Loader)
We have Source Code for everything except EFlash Loader… What’s really happening inside EFlash Loader?
Can we uncover the inner workings of EFlash Loader?
Yes we can!
Bouffalo Lab (creator of BL602) has recently uploaded the ELF Executable for EFlash Loader (pic above). Which makes Reverse Engineering much easier.
(Because of the debugging symbols inside)
Let’s decompile the EFlash Loader ELF with Ghidra!
This is how we decompile the EFlash Loader ELF eflash_loader.elf with Ghidra…
(Works for any ELF file actually)
Install Java Dev Kit (JDK) 11 (64-bit)
Download a Ghidra Release File.
Extract the Ghidra Release File.
Launch Ghidra…
## For Linux and macOS
./ghidraRun
## For Windows
ghidraRun.bat
The Ghidra Help Window appears, with plenty of useful info that’s not available elsewhere.
Minimise the Ghidra Help Window for now.
(But remember to browse it when we have the time!)
In the Ghidra Main Window, click File → New Project
For Project Type: Select Non-Shared Project
For Project Name: Enter “My Project”
Click File → Import File
Select our ELF File: eflash_loader.elf
Ghidra detects that our RISC-V Executable is RV32GC.
Click OK and OK again.
Double-click our ELF File: eflash_loader.elf
The CodeBrowser Window appears.
(With a dragon-like spectre)
When prompted to analyze, click Yes and Analyze.
Ignore the warnings.
(We’ll browse the decompiled C code shortly)
And we’re done with the decompilation! (Screenshot above)
In case of problems, check these docs…
Also check the Ghidra Help Window that we have minimised.
Ghidra has decompiled our ELF File into C code. To export the C code to a file…
In the CodeBrowser Window, click File → Export Program
For Format: Select C / C++
Click OK
(Pic above)
We’ll get a C Source File with roughly 10,000 lines of code…
Which is rather cumbersome to navigate, so we’ll use the Ghidra CodeBrowser to browse our C code in a while.
Ghidra says our executable is RV32GC. Shouldn’t it be RV32IMACF?
BL602 Executables are compiled for the RV32IMACF RISC-V Instruction Set and Extensions…
Designation | Meaning |
---|---|
RV32I | 32-bit RISC-V with Base Integer Instructions |
M | Integer Multiplication + Division |
A | Atomic Instructions |
C | Compressed Instructions |
F | Single-Precision Floating-Point |
Ghidra thinks our executable is RV32GC, which is all of the above plus Double-Precision Floating-Point.
That’s probably OK for our Reverse Engineering, since our executable won’t have any Double-Precision Floating-Point instructions.
(If we import an ESP32-C3 RISC-V ELF, will Ghidra say it’s RV32IMC? Lemme know!)
Let’s locate the Main Function in our decompiled code…
In the CodeBrowser Window, look for the Symbol Tree Pane at left centre
(Pic above)
Expand “Functions”
Double-click on “entry”
entry is located at 0x2201 0000
, the start address of executable code. Thus it’s the first thing that runs when EFlash Loader starts.
In the Decompile Pane (pic above, right side), we see the decompiled code for the entry function: eflash_loader.c
// EFlash Loader starts here
void entry(void) {
// Init BL602 hardware
SystemInit();
// Init BL602 memory
start_load();
// Run the EFlash Loader
main();
Aha we found the Main function! Double-click on “main”.
The Decompile Pane jumps to the Main Function: eflash_loader.c
// Main Function for EFlash Loader
int main(void) {
// Init BL602 Clock
HBN_Set_ROOT_CLK_Sel(...);
...
// Init EFlash Loader
bflb_eflash_loader_interface_init();
bflb_set_low_speed_flash_clock();
...
// Init Embedded Flash
SFlash_Init(...);
bflb_spi_flash_init(...);
SFlash_GetJedecId(...);
SFlash_Qspi_Enable(...);
...
// Run the EFlash Loader
bflb_eflash_loader_main();
The Decompiled Main Function is surprisingly readable (pic below). Kudos to the Ghidra Team!
This code suggests that all the exciting action happens inside bflb_eflash_loader_main. Which we’ll examine in a while.
All this verbose code hurts my eyes. Can we browse the code graphically?
Yes we can! Follow these steps to render the Call Graph for our Decompiled Function…
Click Graph → Calls
Click the Arrangement drop-down box
(Second drop-down from the right)
Select “Compact Radial”
We’ll see the Call Graph below. Which kinda suggests that something exciting happens inside bflb_eflash_loader_main.
Let’s go there now!
Let’s continue the trail from the Main Function.
In the Decompile Pane (right pane), double-click on bflb_eflash_loader_main.
Inside the decompiled function we see a loop that receives and executes Flashing Commands: eflash_loader.c
// Main Loop for EFlash Loader
int32_t bflb_eflash_loader_main(void) {
// Do Handshake
do {
i = boot_if_handshake_poll(...);
} while (i == 0xfffe);
// If Handshake is OK...
if (i == 0) {
// Init Flashing Commands
bflb_eflash_loader_cmd_init();
NextCommand:
do {
// Receive Flashing Command over UART
do {
boot_if_recv(...);
} while (...);
// Execute Flashing Command
i = bflb_eflash_loader_cmd_process(...);
// Process next command
goto NextCommand;
} while (...);
}
}
The code above calls bflb_eflash_loader_cmd_process to execute the Flashing Command received over UART (from the Firmware Flasher).
Let’s find out how it executes Flashing Commands.
Double-click on bflb_eflash_loader_cmd_process. This code appears: eflash_loader.c
// Execute a Flashing Command with the specified Command ID and parameters
int32_t bflb_eflash_loader_cmd_process(uint8_t cmdid, uint8_t *data, uint16_t len) {
// Omitted: Lookup the Command ID
// in list of Flashing Commands
...
// If Flashing Command is enabled...
if (eflash_loader_cmds[i].enabled == 1 && ...) {
// Execute the Flashing Command
ret = (*eflash_loader_cmds[i].cmd_process)();
return ret;
}
Interesting! We see that EFlash Loader has a list of Flashing Commands: eflash_loader_cmds.
The code above looks up eflash_loader_cmds for the Flashing Command (by Command ID). And executes the command by calling cmd_process.
What are the Flashing Commands supported by EFlash Loader? We’ll find out next.
Recall that eflash_loader_cmds defines the list of Flashing Commands supported by EFlash Loader.
In the Decompile Pane (right pane), double-click on eflash_loader_cmds.
This appears in the Listing Pane (centre pane)…
Hover our mouse over eflash_loader_cmds.
Ghidra says that 24 Flashing Commands are defined inside the array. Let’s decipher them…
Expand the array eflash_loader_cmds to see all 24 Flashing Commands
(See pic above)
For each Flashing Command, hover our mouse as shown above
(Or double-click it)
Ghidra reveals the function that handles the Flashing Command
(Like bflb_eflash_loader_cmd_get_bootinfo)
Now we know all 24 Flashing Commands. Neat!
Here are all 24 Flashing Commands supported by EFlash Loader, as decoded by Ghidra from eflash_loader_cmds…
ID | ASCII | Flashing Command |
---|---|---|
10 | LF | *_get_bootinfo |
21 | ! | *_reset |
30 | 0 | *_erase_flash |
31 | 1 | *_write_flash |
3F | ? | *_write_flash_with_decompress |
32 | 2 | *_read_flash |
34 | 4 | *_xip_read_flash |
3A | : | *_write_flash_check |
3B | ; | *_set_flash_para |
3C | < | *_flash_chip_erase |
3D | = | *_readSha_flash |
3E | > | *_xip_readSha_flash |
40 | @ | *_write_efuse |
41 | A | *_read_efuse |
42 | B | *_read_mac_addr |
50 | P | *_write_mem |
51 | Q | *_read_mem |
71 | q | *_read_log |
60 | ` | *_xip_read_flash_start |
61 | a | *_xip_read_flash_finish |
36 | 6 | *_read_jedec_id |
37 | 7 | *_read_status_register |
38 | 8 | *_write_status_register |
33 | 3 | *_flash_boot |
(*
denotes bflb_eflash_loader_cmd)
7 of the above Flashing Commands are documented in the BL602 ISP Protocol…
ID | Documented Command |
---|---|
10 | Get Boot Info |
3C | Chip Erase |
30 | Flash Erase |
31 | Flash Program |
3A | Flash Program Check |
32 | Flash Read |
3D | SHA256 Read |
The other 17 Flashing Commands are undocumented.
(Which might be interesting for future exploration!)
You can’t tell which way the train went by looking at the tracks… So let’s switch over to the (already documented) Firmware Flasher and understand how it calls the Flashing Commands.
The Firmware Flasher works like a State Machine. Each Flashing State triggers a Flashing Command…
Below are the Flashing States and Flashing Command IDs derived from util_program.go…
Flashing State | ID | On Success |
---|---|---|
ConfigReset | *Reset | |
*Reset | *ShakeHand | |
*ShakeHand | 55 | *BootInfo |
*BootInfo | 10 | *BootHeader |
*BootHeader | 11 | *SegHeader |
*SegHeader | 17 | *SegData |
*SegData | 18 | *CheckImage |
*CheckImage | 19 | *RunImage |
*RunImage | 1A | *Reshake |
*Reshake | 55 | *LoadFile |
*LoadFile | *EraseFlash^ | |
*EraseFlash | 30 | *ProgramFlash |
*ProgramFlash | 31 | *ProgramOK^ |
*ProgramOK | 3A | *Sha256 |
*Sha256 | 3D | *LoadFile |
*ProgramFinish | 55 | *ProgramFinish |
*
denotes Cmd (like CmdReset)
^
denotes multiple states
Now that we have the Flashing States and the Flashing Commands, let’s match them.
Right now we have two interesting lists…
Flashing Commands supported by the EFlash Loader
(As uncovered by Ghidra)
Flashing States for the Firmware Flasher’s State Machine
(By reading the BLOpenFlasher source code)
Let’s match the two lists and find out which Flashing Commands are actually called during flashing…
ID | ASCII | Flashing Command |
---|---|---|
10 | LF | Get Boot Info *_get_bootinfo |
30 | 0 | Flash Erase *_erase_flash |
31 | 1 | Flash Program *_write_flash |
3A | : | Flash Program Check *_write_flash_check |
3D | = | SHA256 Read *_readSha_flash |
(*
denotes bflb_eflash_loader_cmd)
Out of 24 commands, only 5 Flashing Commands are actually called during flashing!
(3C
Chip Erase and 32
Flash Read aren’t used while flashing BL602, according to BLOpenFlasher)
And out of the 5 Flashing Commands, only 1 looks interesting…
Let’s study the Decompiled Code and find out how it writes to the Embedded Flash.
In the Symbol Tree Pane (left centre), enter this into the Filter Box…
bflb_eflash_loader_cmd_write_flash
Double-click on the function bflb_eflash_loader_cmd_write_flash.
This is the decompiled Flashing Command that writes the Flashing Image (received via UART) to Embedded Flash: eflash_loader.c
// Flashing Command that writes Flashing Image to Embedded Flash
int32_t bflb_eflash_loader_cmd_write_flash(uint16_t cmd,uint8_t *data,uint16_t len) {
// Write Flashing Image to Embedded Flash
bflb_spi_flash_program(...);
// Return result to Firmware Flasher
bflb_eflash_loader_cmd_ack(...);
The code above calls bflb_spi_flash_program to write the Flashing Image to the Embedded Flash.
Let’s look inside the function…
In the Decompile Pane (right pane), double-click on bflb_spi_flash_program. This appears: eflash_loader.c
// Write Flashing Image to Embedded Flash
int32_t bflb_spi_flash_program(uint32_t addr,uint8_t *data,uint32_t len) {
// Call BL602 ROM to write to Embedded Flash
SFlash_Program(...);
This function calls SFlash_Program to write to Embedded Flash.
SFlash_Program is defined in the BL602 ROM…
Source Code is available in the BL602 IoT SDK…
We’re all done with our Reverse Engineering of BL602 EFlash Loader! 🎉
Thanks to Ghidra we now know everything about EFlash Loader…
We discovered 24 Flashing Commands supported by EFlash Loader
(17 Flashing Commands are undocumented)
Firmware Flasher runs a State Machine that sends Flashing Commands to EFlash Loader over UART
When EFlash Loader receives the “Flash Program” command from Firmware Flasher, it calls BL602 ROM to write the received image to Embedded Flash
Source Code for BL602 ROM is available, so we already understand how it works
Over the past year we speculated on the inner workings of EFlash Loader…
Finally we know what’s inside!
What happens after the Flashing Image has been written to Embedded Flash?
The Flashing Image is compressed with XZ Compression.
The image is decompressed and mapped to XIP Memory (Executable in Place) by the BL602 Bootloader…
And the new firmware starts running on BL602.
I had fun reverse enginnering the BL602 EFlash Loader… My first time using Ghidra!
And I hope you found this article useful for real-world reverse engineering with Ghidra.
Many Thanks to my GitHub Sponsors for supporting my work! This article wouldn’t have been possible without your support.
Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…
lupyuen.github.io/src/loader.md
This article is the expanded version of this Twitter Thread
Many thanks to BraveHeartFLOSSDev for the inspiration! We previously collaborated on this article…
There are 2 versions of the EFlash Loader ELF File…
eflash_loader.elf (17 Jan 2022)
eflash_loader.elf (1 Nov 2021)
Might be interesting to compare the decompiled code and discover the changes!
Does Firmware Flasher send the EFlash Loader ELF to BL602?
Nope it sends the stripped binary for the EFlash Loader, which is easier to load and run on BL602: eflash_loader_40m.bin
Bouffalo Lab used to provide only the stripped binary for EFlash Loader, not the ELF…
bl_iot_sdk/flash_tool/chips/ bl602/eflash_loader
But since Nov 2021 they started uploading the ELF. Which is how we did the reverse engineering with Ghidra. Lucky us ;-)