# qcdm-efs2 A small command-line client for a Qualcomm modem's EFS2 filesystem, over the DIAG serial port, in one file of Python. List, stat, pull, push, and back up files in the modem's EFS from Linux, no Windows and no QPST. I wrote it to back up and replace the RF band policy files (`/policyman`) on a rooted Sony Xperia 1 VI, after finding that the usual tool, [EfsTools](https://github.com/JohnBel/EfsTools), fails on Linux against the `option`-driver diag port: its native serial layer sets modem control lines on open and the driver rejects the ioctl with `EIO`. `pyserial` opens the same port fine, so this talks DIAG over `pyserial` instead. The EFS2 packet layouts are taken from EfsTools, so the wire format is identical; only the serial handling differs. The full write-up, including the Xperia band-unlock use case, is [here](https://mrgecko.org/blog/2026/enable-us-5g-xperia-1-vi-from-linux). ## Requirements - Python 3 - `pyserial` (`pip install pyserial`) - A rooted phone with a Qualcomm modem, in DIAG mode - Root on the Linux host to bind the serial driver ## Getting the phone onto a serial port Put the phone into a USB composition that exposes the DIAG port (keeps adb): ``` adb shell su -c 'setprop sys.usb.config diag,serial_cdev,rmnet,adb' ``` It re-enumerates as a Qualcomm DIAG device (`05c6:9091`). The kernel does not bind a serial driver on its own, so load `option` and hand it the USB id: ``` sudo modprobe option echo 05c6 9091 | sudo tee /sys/bus/usb-serial/drivers/option1/new_id ``` That composition exposes a few serial nodes and only one is DIAG. You don't have to work out which: `efs2.py` probes each candidate, sends a DIAG version request, and keeps the one that answers. It looks at `/dev/serial/by-id/` interface 0 (`-if00-`) first, then the rest. Set `DIAGPORT` or pass `--port` to override. ## Usage ``` efs2.py [-h] [-p PORT] {ls,stat,pull,push,backup} ... ls list an EFS directory stat stat an EFS file pull download one EFS file push upload/overwrite one EFS file, then verify the read-back backup recursively download an EFS directory ``` Examples: ```sh # see what is in the modem's band-policy folder python3 efs2.py ls /policyman # back up a whole directory (subdirectories included) before changing anything python3 efs2.py backup /policyman ./policyman-backup # download and upload single files python3 efs2.py pull /policyman/band_set_01.xml python3 efs2.py push band_set_01.xml /policyman/band_set_01.xml ``` `push` deletes the target, re-creates it, writes in 1 KB chunks, then reads it back and reports whether it matches. That read-back is the point: a truncated write to the modem EFS is how you get a modem that won't register. ## Warning This writes to the modem's EFS. A bad write to the wrong file can stop the modem from booting or registering. Always `backup` first, and know your recovery path (for Sony phones, reflashing the modem `.sin` files with Newflasher restores stock EFS). Use at your own risk. ## License MIT, see `License.txt`. EFS2 protocol layouts derived from [EfsTools](https://github.com/JohnBel/EfsTools) by JohnBel.