Skip to main content

Command Palette

Search for a command to run...

Technical Deep Dive: Unisoc BSL Protocol, FDL1 and FDL2

Published
3 min read
Technical Deep Dive: Unisoc BSL Protocol, FDL1 and FDL2

The Boot Sequence: Power-On to FDL2

When a Unisoc device (like the SL8541E) powers on, the primary CPU executes code from the primitive ROM (BootROM).

Stage 0: BootROM (BROM)

At power-on, a Unisoc SoC is an "empty shell" running primitive code from a tiny on-chip ROM. To interact with it, we must catch it in Download Mode.

If the device detects a specific key combo (usually Vol-Down) or if the eMMC is blank/corrupted, it enters Download Mode. In this state, it enumerates on USB as a Serial Port (VID 0x1782, PID 0x4D00).

Stage 1: The Handshake

The tool must "wake up" the BROM. There are two primary methods:

Legacy BSL (0x7E): The tool sends a burst of 0x7E bytes. The BROM responds with ~SPRD3W~ or similar. Used by older chips.

// From uflash/src/bsl_protocol.cpp 
bool BslProtocol::handshake(int timeout_ms) { 
    std::cout << "Sending 0x7E handshake (32-byte burst)...\n";
    std::vector<uint8_t> burst(32, 0x7E); // BSL_FRAME_TAG
    dev_.write(burst.data(), burst.size()); // ... wait for "~SPRD3W~" response ... 
}

Modern Host Protocol (0xAE): The tool sends 0xAE 0xAE 0xAE 0xAE. The BROM responds with its version string. This is common in Android 10+ chipsets.

bool BslProtocol::host_handshake(int timeout_ms) { 
    uint8_t cmd_ae[] = { 0xAE, 0xAE, 0xAE, 0xAE }; 
    dev_.write(cmd_ae, 4); // ... device responds with internal version string ... 
}

Boot Service Layer (BSL) Protocol

Once the handshake is complete, we use the BSL Protocol for all further commands. Every command is "framed" with 0x7E tags.

Every packet is framed to ensure integrity over noisy serial/USB lines: [7E] [Type: 2B] [Len: 2B] [Payload: N Bytes] [Checksum/CRC: 2B] [7E]

  • Endianness: Big-Endian for headers, Little-Endian for payload contents.

  • Escaping: If 0x7E or 0x7D appears inside the packet, it is escaped to 0x7D [Byte ^ 0x20].

Key Enums & Opcodes

Command Hex Description
BSL_CMD_CONNECT 0x00 Basic handshake
BSL_CMD_START_DATA 0x01 Prepare for transfer (Name, Address, Size)
BSL_CMD_MIDST_DATA 0x02 Steam data chunks
BSL_CMD_END_DATA 0x03 Finalize transfer & Verify Checksum
BSL_CMD_EXEC_DATA 0x04 Execute code at address
BSL_CMD_READ_FLASH 0x10 Read raw block from flash
BSL_CMD_REPARTITION 0x0B Format and rebuild eMMC GPT

Response Codes

  • BSL_REP_ACK (0x80): Success.

  • BSL_REP_VER (0x81): Version response.

  • BSL_REP_OPERATION_FAILED (0x84): Internal device error.

  • BSL_REP_INVALID_PARTITION (0xFE): Partition name/type mismatch.

Stage 2: FDL1 (Stage 1 Loader)

The BROM is extremely limited—it can only read data into a tiny area of SRAM.

FDL1 is small (~40KB) and resides in internal SRAM. Its primary job is to initialize External DDR RAM.

  1. The tool sends BSL_CMD_START_DATA with the address (usually 0x5000 or 0x4000) and size of fdl1-sign.bin.

  2. Data is streamed in chunks via BSL_CMD_MIDST_DATA. BSL_CMD_EXEC_DATA tells the CPU to jump to the FDL1 address.

After FDL1 executes, the device re-enumerates as a different USB device. The host must re-find the handle.

// Flow in main.cpp
bsl.start_data(0x5000, fdl1_size, fdl1_checksum); // Target SRAM address
bsl.midst_data(fdl1_buffer, chunk_size);
bsl.exec_data(0x5000); // Trigger Jump to 0x5000

Stage 2: FDL2 (DDR Loader)

FDL2 is the full "Flash Kernel". We load fdl2-sign.bin into DDR (usually high addresses like 0x80000000). FDL2 contains the Full BSL implementation, including:

  • eMMC/UFS Drivers.

  • File System support (EXT4/Sparse).

  • Modern Repartitioning logic.

52 views