Skip to main content

Ubuntu 20.04 Partial Flash

Goal: Perform a partial flash of an Ubuntu 20.04 image.

Why?: so you can update the kernel, bootloader, and DTS/DTBO to add display support without overwriting the userdata partition (which contains the Ubuntu root file system and your changes). Any many other reasons as well :)

Caution: All examples below intentionally avoid flashing userdata. This preserves your Ubuntu root FS and any local modifications.


Overview

We’ll use tachyon-tools to work with our “bundle ZIPs” for Tachyon:

  • List partitions and addresses inside a bundle ZIP
  • Validate bundle consistency (XML ↔ payloads)
  • Create a filtered bundle ZIP that contains only the partitions you want to flash (e.g., boot, xbl*, xbl_config*, dtbo*) while keeping manifest.json, patch XMLs, and Firehose loaders correct

This enables targeted (OTA-style) updates—kernel/bootloader/DTS—without disturbing userdata.

Repository: https://github.com/particle-iot/tachyon-tools


Prerequisites

  • Host machine with Python 3.7+
  • A bundle ZIP for Tachyon Ubuntu 20.04 (download links below)
  • Particle CLI installed

Clone the Tooling

git clone https://github.com/particle-iot/tachyon-tools.git
cd tachyon-tools
python3 bundle_partition_filter.py --help

Should output something similar to:


usage: bundle_partition_filter.py [-h] {list,validate,filter-in,filter-out} ...

Filter/list/validate partitions in a bundle ZIP (images/qcm6490/edl).

positional arguments:
{list,validate,filter-in,filter-out}
list List partitions (collapsed) from rawprogram XMLs
validate Check referenced EDL files match actual files in the bundle
filter-in Keep ONLY the listed partitions; remove unreferenced EDL files
filter-out REMOVE the listed partitions; remove now-unreferenced EDL files

options:
-h, --help show this help message and exit

Download an Official Release Image

Use the release bundle as the source you will filter:

# Example RoW release bundle
wget https://linux-dist.particle.io/release/tachyon-ubuntu-20.04-RoW-desktop-formfactor_dvt-1.0.167.zip

You can use any matching NA/RoW and version - examples below reference NA 1.0.172 for clarity.


What’s a “bundle ZIP”?

A bundle ZIP is a flashable archive - it has a lot more files in it than this, but these are the critical files for this discussion:

bundle.zip
├── manifest.json
└── images/qcm6490/edl/
├── rawprogram*.xml # “program” plans for partitions
├── patch*.xml # GPT/metadata fixups
├── prog_firehose_*.elf # Firehose programmer(s)
└── <image payloads> # e.g., dtbo.img, gpt_main.bin, super.img, etc.

The tool helps you inspect partitions, verify references, and filter the bundle while keeping it consistent for flashing.


Quick Start

List

./bundle_partition_filter.py list tachyon-ubuntu-20.04-NA-desktop-formfactor_dvt-1.0.172.zip

Filter-In (keep only)

./bundle_partition_filter.py filter-in mybundle.zip --partitions \
system_a,boot_a,vendor -o out_minimal.zip

Filter-Out (remove)

./bundle_partition_filter.py filter-out mybundle.zip \
--partitions userdata,modem* -o out_no_userdata.zip

Validate

./bundle_partition_filter.py validate out_minimal.zip

Example: Update Kernel, Bootloader, and DTS (Partial Flash for Display Support)

Why these partitions?

  • boot_* — carries the kernel (and associated boot image)
  • dtbo_* — device tree overlays (DTS/DTBO) for display support
  • xbl_*, xbl_config_* — Qualcomm UEFI bootloader stages

Create a filtered bundle with just these partitions (both slots A/B):

python3 bundle_partition_filter.py \
filter-in tachyon-ubuntu-20.04-NA-desktop-formfactor_dvt-1.0.172.zip \
--partitions dtbo_a,dtbo_b,xbl_a,xbl_b,xbl_config_a,xbl_config_b,boot_a,boot_b \
-o tachyon-ubuntu-20.04-NA-desktop-formfactor_dvt-1.0.172-BOOT+KERNEL.zip

Validate the result: (if you want!)

python3 bundle_partition_filter.py validate tachyon-ubuntu-20.04-NA-desktop-formfactor_dvt-1.0.172-BOOT+KERNEL.zip

Then flash the filtered ZIP with your EDL tool as you would a full bundle.
This performs a partial flash (kernel/bootloader/DTS only), preserving userdata.

Tip: Use list (below) against both the source and filtered bundles to confirm which partitions will be touched.


Inspect: List Partitions of Interest

List all partitions in a bundle:

python3 bundle_partition_filter.py list tachyon-ubuntu-20.04-NA-desktop-formfactor_dvt-1.0.172.zip

Example output:

Base             Slot  File Size  LUN  Addr                File                                                 
--------------- ---- --------- --- ------------------ -----------------------------------------------------
ALIGN_TO_128K_1 — 0 B 3 0x0000000000006000
ALIGN_TO_128K_2 — 0 B 5 0x0000000000006000
BackupGPT — 140.00 KB ? N/A (7 files)
PrimaryGPT — 168.00 KB 1 0x0000000000000000 (7 files)
abl A 172.00 KB 4 0x0000000000006000 abl.elf
abl B 172.00 KB 4 0x0000000000106000 abl.elf
aop A 0 B 5 0x0000000003740000
aop B 0 B 5 0x00000000037C0000
apdp — 0 B 6 0x000000002F6ED000
art — 0 B 0 0x0000000000086000
bluetooth A 0 B 5 0x0000000002F40000
bluetooth B 0 B 5 0x0000000003340000
boot A 19.30 MB 6 0x000000002FF54000 qti-ubuntu-robotics-image-qcs6490-odk-boot.img
boot B 19.30 MB 6 0x0000000035F54000 qti-ubuntu-robotics-image-qcs6490-odk-boot.img
cache — 6.10 MB 0 0x0000000000288000 (3 files)
catecontentfv — 0 B 6 0x000000002C089000
catefv — 0 B 6 0x000000002C009000
cdt — 0 B 3 0x0000000000020000
connsec — 0 B 6 0x000000002F62D000
cpucp A 0 B 5 0x0000000003840000
cpucp B 0 B 5 0x0000000003940000
ddr — 0 B 3 0x0000000000040000
devcfg A 52.08 KB 5 0x0000000003A40000 devcfg.mbn
devcfg B 52.08 KB 5 0x0000000003A60000 devcfg.mbn
devinfo — 0 B 6 0x0000000023806000
dip — 0 B 6 0x0000000023807000
dsp A 0 B 6 0x000000001B806000
dsp B 0 B 6 0x000000001F806000
dtbo A 154.66 KB 6 0x000000003BF54000 dtbo.img
dtbo B 154.66 KB 6 0x000000003D754000 dtbo.img
featenabler A 0 B 5 0x0000000003A80000
featenabler B 0 B 5 0x0000000003AA0000
frp — 0 B 0 0x0000000000006000
fsc — 0 B 5 0x0000000000020000
fsg — 0 B 5 0x0000000000040000
hyp A 0 B 5 0x0000000003AC0000
hyp B 0 B 5 0x00000000042C0000
imagefv A 0 B 6 0x000000003EF54000
imagefv B 0 B 6 0x000000003F154000
keymaster A 0 B 5 0x0000000004AC0000
keymaster B 0 B 5 0x0000000004B40000
keystore — 0 B 0 0x0000000000106000
last_parti — 0 B 3 0x0000000000140000
limits — 0 B 6 0x0000000023907000
limits-cdsp — 0 B 6 0x0000000023908000
logdump — 0 B 6 0x0000000026209000
logfs — 0 B 6 0x000000002F72D000
mdcompress — 0 B 6 0x000000002E22D000
misc — 0 B 0 0x0000000000188000
modem A 172.42 MB 6 0x0000000000006000 NON-HLOS.bin
modem B 0 B 6 0x000000000DC06000
modemst1 — 0 B 5 0x0000000000440000
modemst2 — 0 B 5 0x0000000000840000
multiimgoem A 0 B 5 0x0000000004BC0000
multiimgoem B 0 B 5 0x0000000004BC8000
nvdata1 — 0 B 5 0x0000000000C40000
nvdata2 — 0 B 5 0x0000000000DC0000
persist — 0 B 5 0x0000000000F40000
qmcs — 0 B 6 0x000000002A209000
quantumsdk — 0 B 6 0x0000000023A09000
questdatafv — 0 B 5 0x0000000004BD0000
qupfw A 0 B 5 0x0000000005BD0000
qupfw B 0 B 5 0x0000000005BE4000
qweslicstore A 256.00 KB 5 0x0000000005BF8000 qweslicstore.bin
qweslicstore B 256.00 KB 5 0x0000000005C38000 qweslicstore.bin
recovery — 19.30 MB 0 0x0000000010A88000 qti-ubuntu-robotics-image-qcs6490-odk-boot.img
recoveryfs — 0 B 0 0x0000000014A88000
rtice — 0 B 6 0x000000002F66D000
secdata — 0 B 6 0x000000002FF4D000
shrm A 0 B 5 0x0000000005C78000
shrm B 0 B 5 0x0000000005C98000
splash — 0 B 6 0x000000003F354000
ssd — 0 B 0 0x0000000000186000
storsec — 0 B 6 0x000000002FF2D000
system A 11.00 GB 0 0x0000000124A88000 qti-ubuntu-robotics-image-qcs6490-odk-sysfs_1.ext4
systemrw — 4.18 MB 0 0x0000000010288000 qti-ubuntu-robotics-image-qcs6490-odk-systemrw_1.ext4
toolsfv — 0 B 6 0x0000000023909000
tz A 0 B 5 0x0000000005CB8000
tz B 0 B 5 0x00000000060B8000
tzsc — 0 B 6 0x000000002F64D000
uefisecapp A 0 B 5 0x00000000064B8000
uefisecapp B 0 B 5 0x00000000066B8000
uefivarstore — 0 B 4 0x0000000000206000
userdata — 67.48 MB 0 0x0000000024A88000 (39 files)
vm-data — 0 B 6 0x000000002C189000
xbl A 3.52 MB 1 0x0000000000006000 xbl.elf
xbl B 3.52 MB 2 0x0000000000006000 xbl.elf
xbl_config A 220.18 KB 1 0x000000000038B000 xbl_config.elf
xbl_config B 220.18 KB 2 0x000000000038B000 xbl_config.elf

CLI Reference

list

Show partitions (from rawprogram*.xml) collapsed by base name and slot.

bundle_partition_filter.py list <bundle.zip>
[--sort-by name|addr]
[--partitions P1,P2,*]
[--slot a|b]
[--ignore-case]
[--print-lun]
[--use-lun-address | --use-phy-address]
  • Columns: Base | Slot | File Size | LUN | Addr | File
  • --sort-by addr sorts by (LUN, address)
  • --use-phy-address shows a heuristic physical address (adds a sequential LUN base offset)
  • --print-lun prints a LUN summary table before the list

validate

Check XML ↔ EDL file consistency. Warn if manifest.json lists XMLs not present.

bundle_partition_filter.py validate <bundle.zip>
[--allow-extra FILE1,FILE2]

Returns non-zero if:

  • Missing: XML references a file not present
  • Extra: A file exists but is not referenced (unless whitelisted)

--allow-extra extends the default whitelist (Firehose loaders are already whitelisted).

filter-in / filter-out

Create a filtered bundle that remains flash-consistent.

bundle_partition_filter.py filter-in  <bundle.zip> --partitions P1,P2,... -o output.zip
[--slot a|b] [--ignore-case] [--strict]

bundle_partition_filter.py filter-out <bundle.zip> --partitions P1,P2,... -o output.zip
[--slot a|b] [--ignore-case] [--strict]
  • filter-in keeps only the matched partitions; filter-out removes them
  • Rewrites manifest.json (program_xml / patch_xml) to reflect what remains
  • Drops any rawprogram*.xml that becomes empty after filtering
  • Prunes unreferenced EDL payloads (Firehose loaders are always kept)
  • Runs validate on the result; non-zero exit on inconsistency
  • --strict: if any exact name in --partitions matched nothing, exit with error

Matching (slots, wildcards, case):

  • Exact names by default (system_a)
  • Wildcards via fnmatch (modem*, *_b)
  • --slot a|b auto-matches *_a or *_b (OTA-style)
  • --ignore-case for case-insensitive matching

Behavior & Implementation Notes

  • Patch XMLs are never filtered; they stay even if you remove all data partitions
  • Firehose loaders are always preserved, even if unreferenced:
    • prog_firehose_ddr.elf, prog_firehose_lite.elf, prog_firehose.elf
  • Pruning: any images/qcm6490/edl/* payload file not referenced by remaining XMLs is removed (except Firehose)
  • Empty rawprogram*.xml files are removed from the ZIP
  • manifest.json sync: program_xml/patch_xml arrays list only XMLs that remain
  • Address display: LUN-relative is the ground truth; physical mode adds a heuristic cumulative LUN offset for visualization only

Additional Examples

List only slot A, sorted by address:

./bundle_partition_filter.py list bundle.zip --slot a --sort-by addr --print-lun

Keep a minimal set for slot A:

./bundle_partition_filter.py filter-in bundle.zip   --partitions system_a,boot_a,vendor,dtbo_a   --slot a   -o out_slotA_min.zip

Remove user data and modem-related partitions (case-insensitive):

./bundle_partition_filter.py filter-out bundle.zip   --partitions userdata,modem*   --ignore-case   -o out_nodata.zip

Whitelist an extra diagnostic blob during validation:

./bundle_partition_filter.py validate out.zip --allow-extra my_diag.bin,readme.txt

Pipe to a file / use in CI:

./bundle_partition_filter.py list bundle.zip --slot b --sort-by addr > partitions.txt

Exit Codes

  • 0 — Success
  • 3 — Validation mismatch (missing/extra EDL files after build)
  • 4--strict exact-partition mismatch (one or more exact names not found)
  • Other non-zero — generic error or parse failure

Troubleshooting

Symptom: Flash tool says Device not responding or Firehose parse error like
Entity: line 1: parser error : Start tag expected, '<' not found.

  • Ensure the filtered ZIP still contains a Firehose loader (these are always kept):
unzip -l out.zip | egrep 'images/qcm6490/edl/prog_firehose.*\.elf$'
  • Compare programmer ELF with the original bundle:
unzip -p original.zip images/qcm6490/edl/prog_firehose_ddr.elf | shasum -a 256
unzip -p out.zip images/qcm6490/edl/prog_firehose_ddr.elf | shasum -a 256

Symptom: validate fails with missing files.

  • A rawprogram*.xml references a payload not present. Ensure your filter didn’t remove needed files (or names match).
  • Use --allow-extra for benign extras; do not whitelist missing files—fix the bundle or XML.

Symptom: validate warns that manifest.json lists XMLs not in EDL.

  • The tool rewrites manifest.json on filter; if you edited it manually or shipped a stale manifest, re-run filtering or fix the manifest.

Device handshake flakiness (outside this tool):

  • Verify device is truly in EDL/9008 mode, swap USB cable/port, avoid hubs, ensure stable power, and make sure no other process holds the interface.

Performance Tips

  • Large bundles: run from a fast disk (SSD)
  • Avoid in-place overwrite on the first try; write to a new file so you can diff
  • Use selective filters (e.g., --slot plus a few partitions) to reduce write time