Project

General

Profile

EC20/EC25 Delta Firmware over the air (DFOTA) upgrades

An example of the DFOTA file is here: http://dfota.quectel-service.com:8088/Dfiles/EC20/EC20EQAR02A05V03-R02A09V02.zip. EC25 seems to use the stock "recovery"/"applypatch".

Mechanism

It is based on the recovery.git of Android. For the EC20 the delta format (and RSA signing) has been replaced by the usage of
RedBend FOTA. The RedBend FOTA is used by other Qualcomm customers too and is also used on the UC20. For the EC25 it
seems that the stock Android/LinuxFoundation recovery is used.

The general approach is:

  • APN is configured for the Linux system
  • AT+QFOTADL is handled by atfwd_daemon and starts wget to write to update.zip
  • "IP_START_UPDATE" > /cache/fota/ipth_config_dfs.txt
  • Reboot into the recovery image
  • recovery will apply the update.

Start delta upgrade

AT+QICSGP=1,1,"APN_NAME_HERE","","",1
AT+QFOTADL="http://dfota.quectel-service.com:8088/Dfiles/EC20/EC20EQAR02A05V03-R02A09V02.zip" 

It will start an internal connection manager to terminate data on the Linux module and then run wget to store the file to
"update.zip". If it worked a reboot into the recovery system will be made.

Recovery image

The /usr/bin/recovery of the recovery image will be started on boot and checks for a update.zip and
the instructions in ipth_config_dfs.txt and then apply the updates. Maintain some counters for how often
an upgrade was attempted and reboot into the main image.

Delta format

Delta updates can update images (e.g. bootloader, kernel) and files on a mounted filesystem. The update.zip
contains one file per image/filesystem to update. The general format is the same for both images and filesystems
and the semantic comes from the partition table supplied by the recovery application to the RedBend component.

The delta update consists out of several nested parts and pointers/offsets into it.

  • Part1 with actual update
    • CRC32 + len
    • Common header
    • Information of decompressed size of TableOfContents (TOC), offset for info within TOC
    • Information of different amount of updates (diffs, inserts, removals, links, etc.)
    • Variable part depending on above information (e.g. old and new CRC32 of files, unused data for insert, unknown for other)
    • LZMA TableOfContents (TOC) (needs to have uncompressed size in the header and no end-of-payload marker)
      • Starts with null terminated filenames to patch
      • List of file permissions (separated by 0xAF)
      • Information about updates (common header points to this offset). For inserts includes filesize, offset for permission, compressed size of update)
    • LZMA of Insert (same requirement as above)
    • Maybe some padding.. unknown about when to insert it
  • Part2 with FileNamesTable
    • CRC32 + len
    • Common Information
    • Len + null terminated string
struct update_part_header {
   uint32_t crc32 // CRC32 of len + data
   uint32_t __le len; //
   uint8_t data[0];
};

We have a python script to dissect the basic structure of a diff and another script to pack a file as insert in an upgrade. Image
updates were not studied.

Kicking the recovery binary

  • mount /dev/mtdblock13 /mnt..
  • Place update.zip to /cache/
  • echo "IP_START_UPDATE" > /cache/fota/ipth_config_dfs.txt to say what to do

Was difficult to repeat, e.g. creating backup and mounting system partition failed on second/third tries

/ # recovery 
mount: mounting none on /sys/kernel/debug failed: Device or resource busy
Starting recovery on Fri Oct 14 12:39:48 2016
recovery filesystem table
=========================
  0 /tmp ramdisk (null) (null) 0
  1 / auto rootfs (null) 0
  2 /proc proc proc (null) 0
  3 /dev/pts devpts devpts (null) 0
  4 /proc/bus/usb usbfs usbfs (null) 0
  5 /dev/shm tmpfs tmpfs (null) 0
  6 /cache yaffs2 /dev/mtdblock9 (null) 0
  7 /media/card auto /dev/mmcblk0p1 (null) 0
  8 /system yaffs2 /dev/mtdblock14 (null) 0
  9 /data yaffs2 /dev/mtdblock15 (null) 0
  10 /misc mtd /dev/mtdblock10 (null) 0

rootfs on / type rootfs (rw)
/dev/root on / type yaffs2 (rw,relatime)
proc on /proc type proc (rw,relatime)
sysfs on /sys type sysfs (rw,relatime)
tmpfs on /dev type tmpfs (rw,relatime,size=64k,mode=755)
devpts on /dev/pts type devpts (rw,relatime,mode=600)
tmpfs on /dev/shm type tmpfs (rw,relatime,mode=777)
/dev/mtdblock15 on /usr type yaffs2 (rw,relatime)
/dev/mtdblock9 on /cache type yaffs2 (rw,relatime)
/dev/mtdblock13 on / type yaffs (rw,relatime)
tmpfs on /dev type tmpfs (rw,relatime,size=64k,mode=755)
/dev/mtdblock9 on /cache type yaffs2 (rw,relatime)
none on /sys type sysfs (rw,relatime)
none on /sys/kernel/debug type debugfs (rw,relatime)
proc on /proc type proc (rw,relatime)
total 3
lrwxrwxrwx    1 root     root            31 Jul 21 07:10 boot_hsic_composition -> /usr/bin/usb/compositions/empty
lrwxrwxrwx    1 root     root            30 Jul 21 07:10 boot_hsusb_composition -> /usr/bin/usb/compositions/9215
drwxr-xr-x    1 root     root          2048 Jul 21 07:10 compositions
crw--w----    1 root     root      247,   0 Jan  8  1970 /dev/ttyGS0
RB_Progress: fwrite "/dev/ttyGS1" fail
I:Checking delta update status...
handle_redbend_update: START_DELTA_UPDATE
I:Setting delta update status...
I:Delta update status is set to (IP_PREVIOUS_UPDATE_IN_PROGRESS 0)
I:Start delta update...
I:Setting recovery boot...
I:Recovery mode reached maximum retry. Clear boot message.
mtd: successfully wrote block at bebe0f4800000000
I:Set boot command "" 
I:boot.command=
I:boot.recovery=
I:Update location: /cache/update.zip
I:number of files in zip is 1 
I:verifying file at index 0
I:No radio diff images found 
I:system.diff found 
E:No modem package available.
E:No modem update needed. returning O.K
mtd: successfully wrote block at bebe23a000000000
I:Reset FOTA cookie done.
start fota update (/cache/fota/system.diff)
redbend_fs_entry: device_name: /dev/mtdblock14
redbend_fs_entry: mount_point: /tmp/system
redbend_fs_entry: update_name: /cache/fota/system.diff
redbend_fs_entry: part_name: system

RB_GetDelta: offset 0x0(0), size 0x14(20)
RB_GetDelta: offset 0x0(0), size 0x18(24)
RB_GetDelta: offset 0x4(4), size 0x3508(13576)
RB_GetDelta: offset 0x0(0), size 0x40(64)
RB_GetDelta: offset 0x350c(13580), size 0x18(24)
RB_GetDelta: offset 0x3510(13584), size 0x27(39)
RB_GetDelta: offset 0x0(0), size 0x14(20)
RB_GetDelta: offset 0x0(0), size 0x18(24)
RB_GetDelta: offset 0x350c(13580), size 0x18(24)
RB_GetDelta: offset 0x0(0), size 0x18(24)
RB_GetDelta: offset 0x0(0), size 0x18(24)
RB_GetDelta: offset 0x350c(13580), size 0x18(24)
RB_GetDelta: offset 0x3524(13604), size 0x4(4)
RB_GetDelta: offset 0x3528(13608), size 0x4(4)
RB_GetDelta: offset 0x352c(13612), size 0x7(7)
RB_GetDelta: offset 0x0(0), size 0x40(64)
RB_GetDelta: offset 0x0(0), size 0x40(64)
RB_GetDelta: offset 0x40(64), size 0x304(772)
RB_GetDelta: offset 0x344(836), size 0xd(13)
RB_GetDelta: offset 0x351(849), size 0x4a8(1192)
FS partition delta dump
RedBend: Delta Info: delta_sig - 0x6d0d05e5
RedBend: Delta Info: delta_size - 13580
RedBend: Delta Info: ver - 82000
RedBend: Delta Info: scout_ver - 80000
RedBend: Delta Info: flags - 0x80000000
RedBend: Delta Info: runtype_flags - 0x280
RedBend: Delta Info: ram_size - 0x200c8
RedBend: Delta Info: sector_size - 0x40000
RedBend: Delta Info: dic_sz - 0xdc8
RedBend: Delta Info: compress_sz - 0x4b5
RedBend: Delta Info: min_alloc_ram_use - 0x22
RedBend: Delta Info: ext_info_sz - 2
RedBend: Delta Info: num_copy - 0
RedBend: Delta Info: num_diff - 95
RedBend: Delta Info: num_insert - 3
RedBend: Delta Info: num_delete - 0
RedBend: Delta Info: num_del_dirs - 0
RedBend: Delta Info: num_dirs - 0
RedBend: Delta Info: num_del_link - 0
RedBend: Delta Info: num_link - 4
RedBend: Delta Info: num_critical_update - 95
RedBend: Delta Info: num_critical_insert - 3
RB_CreateFolder: /cache/fota/a, mode:0x1ff
open file /cache/fota/a/backup
RB_OpenFile: Path:/cache/fota/a/backup | Mode: RDONLY 
 First open() with error 2
open file /tmp/system/etc/version
RB_OpenFile: Path:/tmp/system/etc/version | Mode: RDONLY 
 First open() with error 2
open file /tmp/system/etc/version
RB_OpenFile: Path:/tmp/system/etc/version | Mode: RDONLY 
 First open() with error 2
RedBend: Error in scout, file signature mismatch in file /tmp/system/etc/version
umount: can't umount /tmp/system: No such file or directory
fota update fail (/cache/fota/system.diff)
I:fs:/cache/update.zip update err
mtd: successfully wrote block at bebe23a000000000
I:Reset FOTA cookie done.
mtd: successfully wrote block at bebe0f3800000000
I:Set boot command "" 
I:Setting delta update status...
I:Delta update status is set to (IP_PREVIOUS_UPDATE_FAILED 500)
handle_redbend_update: DELTA_UPDATE_IN_PROGRESS
I:Setting delta update status...
I:Delta update status is set to (IP_PREVIOUS_UPDATE_IN_PROGRESS 0)
I:Start delta update...
I:Setting recovery boot...
I:Error opening recovery count file. Ignore.
mtd: successfully wrote block at bebe0f4800000000
I:Set boot command "boot-recovery" 
I:boot.command=boot-recovery
I:boot.recovery=recovery

I:Update location: /cache/update.zip
E:Can't open /cache/update.zip
(No such file or directory)
mtd: successfully wrote block at bebe0f3800000000
I:Set boot command "" 
I:Setting delta update status...
I:Delta update status is set to (IP_PREVIOUS_UPDATE_FAILED 410)
mtd: successfully wrote block at bebe23a000000000
I:Reset FOTA cookie done.
RB_Progress: fwrite "/dev/ttyGS1" fail
precent:0 total:1 cur:1
Rebooting after recovery
I:Rebooting at the end of recovery module.
mtd: successfully wrote block at bebe0f8000000000
I:Set boot command "" 
Add picture from clipboard (Maximum size: 48.8 MB)