MediaTek Details: Preloader

NOTE: The information was gathered through reverse engineering.

After the Boot ROM in a SoC finishes initalizing the bare hardware, it loads the first region of the eMMC/NAND flash into the on-chip SRAM and starts executing it. This would be the bootloader but on MediaTek devices it is not, there is a another step for loading the main Bootloader: the Preloader. This is like a bridge between the Boot ROM and the main Bootloader and offers some features such as entering Download Mode (in BROM) from the Preloader if enabled, boot from eMMC or NAND flash and read/write parts of the flash via USB.

The overview

Usually when MediaTek ships a source package to a manufacturer, it also contains the entire source code for the Preloader. It is not easy to come up with a generic description of what the Preloader does because the manufacturer can modify it, so it does different things on different SoCs and Devices.

Based on the lightning72_wet_l source code for the MT6572 SoC, the preloader is made up of the following parts:

● SoC-specific stuff in mediatek/platform/${platform}/preloader, this contains the core, drivers, initalizing and security code.

● Device-specific stuff in mediatek/custom/${device}/preloader, this contains the Device-specific code.

Now it's time to dive deeper!

SoC initializing

When the Preloader gets executed, many stuff are in a random state so to initialize them, there is some Assembly code located in mediatek/platform/${platform}/preloader/src/init/init.s which performs the following steps:

1. Clear all registers.

2. Switch the CPU to SVC32 mode.

3. Disable interrupts.

4. Setup the caches and stack.

5. Duplicate the CTP code to the Slave CPU and start it.

6. Creates a VMA-to-LMA mapping for the Slave CPU.

7. Jump to the main code in the C code.

Execution now continues at mediatek/platform/${platform}/preloader/src/core/main.c, which calls lots of other methods and start the rest of the Preloader code (security, drivers and the rest of the core).

Platform initializing

The Preloader relies on some peripherials so it has to initialize them. This is mainly done with the platform_pre_init and platform_init code. The list contains the timer, PLL clock, DDR memory controller, Watchdog, GPIO, UART, USB port and the PMC.

There is something special in the Preloader if enabled: after the flash storage has been initialized, the Preloader offers a early "Download" mode which the manufacturer can define a hardware key that if pressed in 1000 ms, it will trigger a Assert which will cause the Preloader to error out and then reboots back into the Boot ROM in Download Mode. However there is a timeout of 5 seconds before being brought back to the Preloader that can be extended by sending the Start command sequence, to disable the timeout the Watchdog will have to be disabled.

The Preloader also records why the system was booted:

typedef enum {

    BR_POWER_KEY = 0,

    BR_USB,

    BR_RTC,

    BR_WDT,

    BR_WDT_BY_PASS_PWK,

    BR_TOOL_BY_PASS_PWK,

#ifdef RTC_2SEC_REBOOT_ENABLE

    BR_2SEC_REBOOT,

#endif

    BR_UNKNOWN

} boot_reason_t;

Partition table

After initializing the platform, the Preloader now has entire full access to the eMMC/NAND flash. MediaTek decided to partition the flash, but the table is hardcoded. It is generated during a Preloader generation session from a real Excel file in mediatek/build/tools/ptgen/${platform}/partition_table_${platform}.xls by the command ./makeMtk -t ${device} ptgen. (here is a example of the partition table for a device named PE900s)



The partition table structure is stored into (for PE900s) mediatek/custom/pe900s/preloader/cust_part.c and looks like this:

static part_t platform_parts[PART_MAX_COUNT] = {

        {PART_PRELOADER, 0, PART_SIZE_PRELOADER, 0,PART_FLAG_NONE},

        {PART_MBR, 0, PART_SIZE_MBR, 0,PART_FLAG_NONE},

        {PART_EBR1, 0, PART_SIZE_EBR1, 0,PART_FLAG_NONE},

        {PART_PRO_INFO, 0, PART_SIZE_PRO_INFO, 0,PART_FLAG_NONE},

        {PART_NVRAM, 0, PART_SIZE_NVRAM, 0,PART_FLAG_NONE},

        {PART_PROTECT_F, 0, PART_SIZE_PROTECT_F, 0,PART_FLAG_NONE},

        {PART_PROTECT_S, 0, PART_SIZE_PROTECT_S, 0,PART_FLAG_NONE},

        {PART_SECURE, 0, PART_SIZE_SECCFG, 0,PART_FLAG_NONE},

        {PART_UBOOT, 0, PART_SIZE_UBOOT, 0,PART_FLAG_NONE},

        {PART_BOOTIMG, 0, PART_SIZE_BOOTIMG, 0,PART_FLAG_NONE},

        {PART_RECOVERY, 0, PART_SIZE_RECOVERY, 0,PART_FLAG_NONE},

        {PART_SECSTATIC, 0, PART_SIZE_SEC_RO, 0,PART_FLAG_NONE},

        {PART_MISC, 0, PART_SIZE_MISC, 0,PART_FLAG_NONE},

        {PART_LOGO, 0, PART_SIZE_LOGO, 0,PART_FLAG_NONE},

        {PART_EXPDB, 0, PART_SIZE_EXPDB, 0,PART_FLAG_NONE},

        {PART_ANDSYSIMG, 0, PART_SIZE_ANDROID, 0,PART_FLAG_NONE},

        {PART_CACHE, 0, PART_SIZE_CACHE, 0,PART_FLAG_NONE},

        {PART_USER, 0, PART_SIZE_USRDATA, 0,PART_FLAG_NONE},

        {PART_FAT, 0, PART_SIZE_FAT, 0,PART_FLAG_NONE},

        {NULL,0,0,0,PART_FLAG_END},

};

Secure boot stage (part 1)

After loading all the partitions (and if Secure boot is enabled), the Preloader initializes the SecLib subsystem. The OEM supplies a RSA key up to 2048 bits in length.

It is unknown on what SecLib does exactly. It takes data from the SECURE partition (if it exists) and the RSA key, and then calls in the binary blob mediatek/platform/${platform}/preloader/src/SecLib.a.

The boot mode selection

After confirming Secure boot (optional), the Preloader decides which boot mode it will go for.

NORMAL_BOOT is used if secure boot is disabled or the module does not say otherwise. If Download Mode is enabled it will try to enter it immediately. The list for other boot modes is long, but not all of them are self-explanatory:

typedef enum

{

    NORMAL_BOOT = 0,

    META_BOOT = 1,

    RECOVERY_BOOT = 2,

    SW_REBOOT = 3,

    FACTORY_BOOT = 4,

    ADVMETA_BOOT = 5,

    ATE_FACTORY_BOOT = 6,

    ALARM_BOOT = 7,

#if defined (MTK_KERNEL_POWER_OFF_CHARGING)

    KERNEL_POWER_OFF_CHARGING_BOOT = 8,

    LOW_POWER_OFF_CHARGING_BOOT = 9,

#endif

    FASTBOOT = 99,

    DOWNLOAD_BOOT = 100,

    UNKNOWN_BOOT

} BOOTMODE;

BootROM Download Mode

Before Download Mode can be entered, the Preloader needs to find out if a host is connected via USB/UART and running the SP Flash Tool. Through it can be bypassed by pressing a BROM key while plugging the device in. For SP Flash Tool, it does this by configuring a virtual CDC ACM discipline in USB, so both lines are serial ports and behave the same.

The USB port if connected will assume that the SP Flash Tool is connected if it receives a CDC message. It then sends the READY string to the tool and waits for a token of 8 bytes.

If detected successfully, the tool will send a special Start command sequence (0xa0 0x0a 0x50 0x05) to enter a special mode that is only available via USB or holding the BROM button while plugging the USB in. It incorporates the following commands:

Command Byte Function
CMD_GET_HW_SW_VER 0xfc Return hardware subcode, hardware/software version
CMD_GET_HW_CODE 0xfd Return hardware code/status
CMD_GET_BL_VER 0xfe Gets the Preloader version
CMD_LEGACY_WRITE 0xa1 Write data to the SoC memory (legacy)
CMD_LEGACY_READ 0xa2 Read data from the SoC memory (legacy)
CMD_I2C_INIT 0xb0 Initializes the I2C
CMD_I2C_DEINIT 0xb1 Shuts down the I2C
CMD_I2C_WRITE8 0xb2 Write data to the I2C (8 bit length)
CMD_I2C_READ8 0xb3 Read data from the I2C (8 bit length)
CMD_I2C_SET_SPEED 0xb4 Sets the I2C speed
CMD_PWR_INIT 0xc4 Initializes the PMC
CMD_PWR_DEINIT 0xc5 Shuts down the PMC
CMD_PWR_READ16 0xc6 Read 16 bit data from the PMC interface memory (16 bit length)
CMD_PWR_WRITE16 0xc7 Write 16 bit data to the PMC interface memory (16 bit length)
CMD_READ16 0xd0 Read data from the SoC memory (16 bit length)
CMD_READ32 0xd1 Read data from the SoC memory (32 bit length)
CMD_WRITE16 0xd2 Write data to the SoC memory (16 bit length)
CMD_WRITE16_NO_ECHO 0xd3 Write data to the SoC memory without echoing (16 bit length)
CMD_WRITE32 0xd4 Write data to the SoC memory (32 bit length)
CMD_JUMP_DA 0xd5 Sets boot mode to DOWNLOAD_BOOT and starts execution of the Download Agent sent to the SoC
CMD_JUMP_BL 0xd6 Starts execution of the Preloader on eMMC/NAND flash
CMD_SEND_DA 0xd7 Sends a "Download Agent" binary to the SoC signed with a key
CMD_GET_TARGET_CONFIG 0xd8 Gets supported Preloader config flags
CMD_UART1_LOG_EN 0xdb Not sure what this command does


The Download Agent step is necessary so this way the SP Flash Tool can send a current version for the exact hardware version that is being used.

The UART has no possibility to detect if the physical line is powered, so it will just send the READY string and hopes for a 8 byte token. If it does, then it assumes that the tool is present.

NOTE: The special commands from the table above are not available when using UART, maybe because the BROM

already offers most of these commands via UART.

If the Start command is not issued with the host via USB, the Preloader will enter a common mode in which it incorporates the following commands over both USB/UART:

Command String Function
HSNK_COM_READY READY If sent, will wait for a 8 byte token before switching to BROM
META_STR_REQ METAMETA Switches into META_BOOT mode
META_STR_ACK ATEMATEM Used for response from META_BOOT mode
META_LOCK LOCK Not sure what this command does
META_FORBIDDEN_ACK METAFORB Not sure what this command does
META_ADV_REQ ADVEMETA Switches into ADVMETA_BOOT mode
META_ADV_ACK ATEMEVDA Used for response from ADVMETA_BOOT mode
FACTORY_STR_REQ FACTFACT Switches into FACTORY_BOOT mode
FACTORY_STR_ACK TCAFTCAF Used for response from FACTORY_BOOT mode
ATE_STR_REQ FACTORYM Switches into ATE_FACTORY_BOOT mode
FACTORY_STR_ACK MYROTCAF Used for response from ATE_FACTORY_BOOT mode
SWITCH_MD_REQ SWITCHMD Switches the modem into firmware download mode
SWITCH_MD_ACK DMHCTIWS Used for response from modem in firmware download mode
ATCMD_NBOOT_REQ AT+NBOOT Switches into NORMAL_BOOT mode
ATCMD_OK AT+OK Not sure what this command does
ATCMD_UNKNOWN AT+UNKNOWN Not sure what this command does
FB_STR_REQ FASTBOOT Switches into FASTBOOT mode
FB_STR_ACK TOOBTSAF Used for response from FASTBOOT mode

Secure boot stage (part 2)

Once again it is unknown what SecLib does at this stage, it just calls into the binary blob most of the time its active.

The following (questionable) info was gathered by looking at a C wrapper and dumping the library symbols and strings:

● Security data comes from the SECSTATIC partition

● Validation of cryptographic image signatures using RSA/SHA1

● (CONFIRMED AS SEEN HERE AS FLASHING LK IN MAVERICK GETS IT STUCK IN PRELOADER) The UBOOT, LOGO, BOOTIMG, RECOVERY and ANDROID partitions are checked at some point

● The "customer name" seems to be checked, strange but why?

The necessary signed images are likely generated by the SignTool binaries in mediatek/build/tools/SignTool.

The OEM can likely add additional security measures.

Loading core boot images

Now that the Preloader knows the system is good to go, safe and secure, it can load the firmware images from the eMMC/NAND flash.

This is highly specialised process, because every image has to be processed differently. For example the firmware for the HSPA modem in the MT6582 has to be fed into the modem by using special registers and commands, while the LK bootloader can just be copied to the correct memory address. In this step the Ppreloader will decide also which is the next component that gets executed after itself ends. This will be the bootloader image stored in the UBOOT partition by default.

NOTE: Only the most basic firmware (like modem and LK) is loaded.

Platform post initializing

In this step, the platform is put into a state for the LK. The most important step for the Preloader is to pass on the boot arguments that was set during the Preloader's execution. This will make more sense once we look at what happens after the Preloader has been executed fully, the whole MediaTek design is a bit compilcated.

The boot arguments structure for the MT6572 will look like this:

typedef struct {

    u32 magic;

    boot_mode_t mode;

    u32 e_flag;

    u32 log_port;

    u32 log_baudrate;

    u8 log_enable;

    u8 reserved[3];

    u32 dram_rank_num;

    u32 dram_rank_size[4];

    u32 boot_reason;

    u32 meta_com_type;

    u32 meta_com_id;

    da_infp_t da_info;

    SEC_LIMIT sec_limit;

} boot_arg_t;
It is put at a memory location where it survives until the next component grabs it.

Booting the next component

The last step the Preloader has to do is jump to LK loaded from the UBOOT partition.

After this, the Preloader goes inactive until the BROM starts again.

Sources

MT6572 source code for PE900s and lightning72_wet_l

Introduction of MTK Tools