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
userdatapartition (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 keepingmanifest.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 supportxbl_*,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 addrsorts by(LUN, address)--use-phy-addressshows a heuristic physical address (adds a sequential LUN base offset)--print-lunprints 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-inkeeps only the matched partitions;filter-outremoves them- Rewrites
manifest.json(program_xml/patch_xml) to reflect what remains - Drops any
rawprogram*.xmlthat becomes empty after filtering - Prunes unreferenced EDL payloads (Firehose loaders are always kept)
- Runs
validateon the result; non-zero exit on inconsistency --strict: if any exact name in--partitionsmatched nothing, exit with error
Matching (slots, wildcards, case):
- Exact names by default (
system_a) - Wildcards via
fnmatch(modem*,*_b) --slot a|bauto-matches*_aor*_b(OTA-style)--ignore-casefor 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*.xmlfiles are removed from the ZIP manifest.jsonsync:program_xml/patch_xmlarrays 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— Success3— Validation mismatch (missing/extra EDL files after build)4—--strictexact-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*.xmlreferences a payload not present. Ensure your filter didn’t remove needed files (or names match). - Use
--allow-extrafor 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.jsonon 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.,
--slotplus a few partitions) to reduce write time