known·good

Notes / firmwaremediatekmt6592sp-flash-toolmtkclientandroidkernellinux

SP Flash Tool on Linux (MT6592): S_COM_PORT_OPEN_FAIL (1013) in BROM, then "PMT changed for the ROM"

· by shaun, written up with Claude

Writing /system on an unrooted MT6592 handheld from Linux. BROM port-open fails because new cdc_acm rejects TIOCCBRK; Download refuses with "PMT changed". Use SP Flash Tool v5.1648, an LD_PRELOAD shim, Preloader mode and write-memory at the physical address.

Tested onArch Linux, kernel 7.2.3 (cdc_acm); SP Flash Tool v5.1648 Linux (console mode); mtkclient (git 2026-09-12); target: MT6592 clone handheld, Android 5.1, 8 GB eMMC, unlocked BROM (no SBC/SLA/DAA)

Symptoms #

Goal: replace one file (/system/media/bootanimation.zip) on an unrooted MediaTek MT6592 Android 5.1 device by writing a modified system image over USB from Linux. Every tool failed in a different way.

mtkclient reads the eMMC fine, but every write hangs forever right after the command header:

DaHandler - Writing offset 0x5000000 with length 0x100000
DeviceClass - [LIB]: TX:62
...
DeviceClass - [LIB]: TX:00100000

In BROM mode with --preloader, it got stuck one step earlier instead:

DALegacy - M_EXT_RAM_SIZE : 0xc000000
DALegacy - Uploading stage 2...
DALegacy - Waiting for response ...

SP Flash Tool (Linux, console mode flash_tool -i config.xml):

  • v5.2228: its MTK_AllInOne_DA.bin has no MT6592 entry.
  • v5.1916: Failed to Connect DA: S_FT_DA_NO_RESPONSE(4001) right after 100% of DA has been sent, in both BROM and Preloader mode.
  • BROM mode (0e8d:0003, vol-up held), even when run as root:
USB port detected: /dev/ttyACM0
Connect BROM failed: S_COM_PORT_OPEN_FAIL(1013)
[COM] Failed to open COM port.
  • Preloader mode (0e8d:2000) with v5.1648, "Download Only" of just the system partition:
DA Connected
Uncaught BaseException in main(): PMT changed for the ROM; it must be downloaded.
Please select "Format All + Download" scene and try again

Do not follow that hint. "Format All + Download" erases the whole device, userdata and NVRAM included.

Root cause #

Four independent problems:

  1. S_COM_PORT_OPEN_FAIL is not a permissions problem. strace shows the open() of /dev/ttyACM0 succeeds. SP Flash Tool then calls ioctl(fd, TIOCCBRK), which returns -1 EOPNOTSUPP, and the tool gives up. Recent kernels' cdc_acm refuses break requests when the device does not advertise break support in its CDC descriptors. The MediaTek BROM does not advertise it, while the Preloader does. That is why only BROM mode fails. Older kernels silently accepted the request, and this 2016–2019 tool assumes that.

    openat(AT_FDCWD, "/dev/ttyACM0", O_RDWR|O_NOCTTY|O_NONBLOCK) = 10
    ioctl(10, TCSETS2, {c_cflag=B115200|CS8|CREAD, ...}) = 0
    ioctl(10, TIOCCBRK) = -1 EOPNOTSUPP (Operation not supported)
    close(10)
    
  2. The DA version matters. The download agent (DA) in SP Flash Tool v5.1916 (MTK_AllInOne_DA_v3.3001.2019-04-24) never answers on this board. The one in v5.1648 (v3.3001.00.00) works. It is byte-identical to the MTK_AllInOne_DA_mt6590.bin that mtkclient ships, which is why mtkclient could always connect and read.

  3. mtkclient can read but not write here. With the legacy DA it sends SDMMC_WRITE_DATA (0x62) and waits for an ACK that never comes, and it has no timeout. Separately, in BROM mode it sends the first DRAM (EMI) entry from the preloader rather than the one matching the eMMC ID. This preloader holds four entries; the matching one was number 3 (LPDDR3, 1 GB). The first one is LPDDR2, so stage 2 crashes (note the M_EXT_RAM_SIZE : 0xc000000). Reordering the entries in a host-side copy of the preloader fixes the DRAM setup, but the write still hangs.

  4. "PMT changed" is SP Flash Tool's own arithmetic. Its detailed log (/tmp/SP_FT_Logs/.../QT_FLASH_TOOL.log) names the partition:

    CheckPMTLayoutChange(): PMT changed for <USRDATA>: addr<0x7f800000>-->addr<0x7f800000>,
    len<0x151300000>-->len<0x151b80000>
    

    The tool auto-sizes the last partition (userdata) to fill the eMMC, but counts boot1 + boot2 + RPMB (4 + 4 + 0.5 MB = 0x880000) into the total. The device's real table, written at the factory, does not. The scatter file can't override this, because the tool recomputes the size.

Fix #

Use the raw write-memory command instead of "Download". It writes a file to an explicit physical eMMC address and skips the partition-table comparison. It is also unforgiving: you own the address, so take a full dump first and verify placement.

1. Dump everything and find the real layout. Use mtkclient (reads work). MT65xx devices keep a "PMT" table near the end of the user area, with magic 1vTP1.1, 96-byte entries (name[64], size u64, region u64 (1 = boot1, 8 = user), offset u64, mask u64), and a mirror 4 KB later. Here ANDROID (system) was at physical 0x5000000, size 0x60000000.

2. Shim the break ioctl so BROM mode works too (Preloader mode does not need it, but it does no harm):

c// brkshim.c — gcc -shared -fPIC -O2 -o brkshim.so brkshim.c -ldl
#define _GNU_SOURCE
#include <dlfcn.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/ioctl.h>
#include <termios.h>

int ioctl(int fd, unsigned long req, ...) {
    static int (*real)(int, unsigned long, ...);
    if (!real) real = dlsym(RTLD_NEXT, "ioctl");
    va_list ap; va_start(ap, req); void *arg = va_arg(ap, void *); va_end(ap);
    int r = real(fd, req, arg);
    if (r < 0 && errno == EOPNOTSUPP && (req == TIOCSBRK || req == TIOCCBRK)) { errno = 0; return 0; }
    return r;
}

3. Run SP Flash Tool v5.1648 in console mode with the shim. It also needs libpng12.so.0, which ships in the lib/ of later SP Flash Tool zips:

shT=/path/to/SP_Flash_Tool_v5.1648_Linux
cd "$T"
sudo env LD_PRELOAD=/path/to/brkshim.so LD_LIBRARY_PATH="$T:$T/lib" \
     QT_QPA_PLATFORM=offscreen ./flash_tool -r -i /path/to/writemem.xml

Then plug the device in powered off, with no buttons held (Preloader mode, 0e8d:2000). In this mode the preloader has already initialised DRAM, so no EMI settings are needed.

4. The write-memory config. A scatter file is still required. Generate it from the PMT (all partitions, linear_start_addr = physical_start_addr) and add skip_pmt_operate: true to its general → info block so the tool never rewrites the table. If the scatter marks nothing is_download: true, the tool aborts during "auto load all roms" (DL_AutoLoad failed). So mark exactly one partition downloadable and give it a harmless, disabled rom (here: the original, unmodified system image):

xml<flashtool-config version="2.0">
  <general>
    <chip-name>MT6592</chip-name>
    <storage-type>EMMC</storage-type>
    <download-agent>/path/to/SP_Flash_Tool_v5.1648_Linux/MTK_AllInOne_DA.bin</download-agent>
    <scatter>/path/to/MT6592_writemem_scatter.txt</scatter>
    <rom-list>
      <rom index="17" enable="false" partition="ANDROID">/path/to/system_original.img</rom>
    </rom-list>
    <connection type="BromUSB" high-speed="false" power="AutoDetect" timeout-count="3600000" com-port="" />
  </general>
  <commands>
    <write-memory>
      <write-memory-item input-mode="FromFile" program-mode="PageOnly"
          addr-mode="PhysicalAddress" address="0x5000000" input-length="0x60000000"
          part-id="8">/path/to/system_new.img</write-memory-item>
    </write-memory>
  </commands>
</flashtool-config>

part-id="8" is the eMMC user area. Only one write-memory-item is allowed per config.

Result: 100% of data write to memory, (1610612736 /1610612736) in about 2.5 minutes (about 10 MB/s), and the device booted with the new boot animation.

Caveats

  • Prove placement first. Before the real write, I wrote 1 MiB of random bytes into the middle of the CACHE partition (disposable), read an 18 MB window back with mtkclient, and confirmed it landed at exactly the requested address with nothing else changed.
  • Always read back. The first full 1.5 GB attempt stalled after one 1 MiB packet (no CPU, no progress for 30+ minutes). It did not happen again on retries (2 MiB, 94 MiB, then 1.5 GB all completed), so I have no explanation for it. A read-back showed exactly one new 1 MiB chunk had been written.
  • The image must be valid for Android: e2fsck -fn clean, and the replaced files must keep their owner, mode and security.selinux xattr (u:object_r:system_file:s0). Check with debugfs -R 'ea_list /media/bootanimation.zip' system.img.
  • A hung DA leaves the device unresponsive. It keeps running on battery and ignores buttons, and the host logs device descriptor read/64, error -71. Hold power for 15–20 seconds to reset it.
  • BROM mode needs vol-up held at the moment the chip powers up. Unplug everything, hold vol-up, then connect the data cable. With external power attached it tends to land in Preloader mode instead.

How it was found #

  • strace -f -e trace=openat,ioctl,read,write on flash_tool showed the TIOCCBRK failure and, later, the per-packet protocol: Z, 1 MiB data, 2-byte checksum, then the device replies i.
  • The kernel log showed BROM enumerating cleanly (cdc_acm …: ttyACM0: USB ACM device), so the cable and port were fine.
  • Comparing sha256 sums of the MTK_AllInOne_DA.bin files showed mtkclient's working DA is the one from v5.1648. Parsing the DA header (0xDADA entries with hw code) showed which tool versions still list MT6592.
  • SP Flash Tool's detailed logs in /tmp/SP_FT_Logs/ give the exact reason behind generic errors such as "PMT changed".

References #

  • mtkclient: https://github.com/bkerler/mtkclient
  • SP Flash Tool Linux builds (older versions are still on the CDN): https://cdn.spflashtools.com/wp-content/uploads/SP_Flash_Tool_v5.1648_Linux.zip