qdl: Flashing over EDL Mode
Bài này dịch từ README của qdl trên GitHub, đối chiếu ngày 23/09/2026, bản phát hành mới nhất lúc đó là v2.8.
qdl (Qualcomm Download) là công cụ dòng lệnh giao tiếp với thiết bị Qualcomm ở chế độ EDL qua USB. Nó nạp một flash loader (programmer) lên thiết bị rồi dùng loader đó để ghi image vào bộ nhớ. Công cụ nhận diện thiết bị EDL qua Vendor ID 05c6 với các Product ID 9008, 900e, 901d và 90db. Mã nguồn dùng giấy phép BSD 3-Clause.
EDL mode là gì
EDL (Emergency Download) là chế độ boot đặc biệt có sẵn trên các SoC Qualcomm, cho phép truy cập mức thấp để nạp firmware và cứu máy. Chế độ này đi vòng qua quá trình boot thông thường, nên vẫn nạp được firmware cả khi thiết bị không boot lên nữa hoặc bootloader đang bị khoá.
Cách đưa board vào EDL mode khác nhau tuỳ thiết bị, phải xem tài liệu của board. Với RB5, xem lại bài Linaro: Flash Bootloader, Boot Image và Rootfs.
Build
Linux
sudo apt install libxml2-dev libusb-1.0-0-dev libzip-dev meson ninja-build help2manmeson setup buildmeson compile -C buildmacOS
Với Homebrew:
brew install libxml2 libusb libzip meson ninja help2manmeson setup buildmeson compile -C buildVới MacPorts:
sudo port install libxml2 libusb libzip meson ninja help2manmeson setup buildmeson compile -C buildWindows
Cài môi trường MSYS2 trước. Sau đó mở terminal MSYS2 MinGW64 (file <thư-mục-cài-msys2>\mingw64.exe) và cài các gói cần thiết bằng pacman:
pacman -S base-devel --neededpacman -S gitpacman -S help2manpacman -S mingw-w64-x86_64-gccpacman -S mingw-w64-x86_64-mesonpacman -S mingw-w64-x86_64-ninjapacman -S mingw-w64-x86_64-libusbpacman -S mingw-w64-x86_64-libxml2pacman -S mingw-w64-x86_64-libzipRồi build bằng meson như trên:
meson setup buildmeson compile -C buildTuỳ chọn khi build
libzip chỉ cần cho phần hỗ trợ zip container: flash thẳng từ file zip và lệnh con create-zip. Tính năng này bật mặc định nên thiếu libzip là bước configure sẽ lỗi. Muốn build gọn hơn, tắt hẳn tính năng zip-container:
meson setup build -Dzip-container=disabledFlash cơ bản
Chạy qdl --help để xem đầy đủ cách dùng. Ví dụ flash một bản build dạng FLAT:
qdl prog_firehose_ddr.elf rawprogram*.xml patch*.xmlNếu máy host đang cắm nhiều board, chỉ định số serial của board cần flash bằng --serial:
qdl --serial=0AA94EFD prog_firehose_ddr.elf rawprogram*.xml patch*.xmlFlash từ installer package
Khi có sẵn installer package thay vì các file binary và XML rời, dùng lệnh con flash:
qdl flash <installer.zip>Nếu package đã giải nén:
qdl flash flashmap.jsonCả hai đều kết hợp được với các tuỳ chọn khác như --serial.
Muốn cài một phần của package, thêm hậu tố ::storage1[,storage2...] vào tên file. Nếu flashmap có nhiều layout, chọn layout bằng hậu tố ::layout-name; kết hợp layout với storage theo dạng ::layout-name/storage1[,storage2...]:
qdl flash installer.zip::layout1/ufsFlash từ contents.xml
qdl cũng flash được các bản build mô tả bằng file contents.xml:
qdl flash contents.xmlVì contents XML có thể mô tả nội dung cho nhiều loại storage và nhiều flavor, đôi khi phải chọn phần cần flash bằng hậu tố ::specifier1,specifier2.... Specifier được so khớp với storage type và flavor. Mỗi storage chỉ được phép có tối đa một specifier khớp, và chỉ phần được chọn mới bị flash. Ví dụ:
qdl flash contents.xml::ufs,safe_rtosLệnh trên flash storage UFS với flavor duy nhất phù hợp, đồng thời flash safe_rtos lên spinor.
Tạo installer package
qdl tạo được installer package từ bản build mô tả bằng contents.xml. File zip sinh ra chứa flashmap.json, các programmer image được chọn, cùng các file rawprogram, patch và image được tham chiếu:
qdl create-zip installer.zip contents.xmlKhi contents XML mô tả nhiều tổ hợp storage hoặc flavor, chọn nội dung mong muốn bằng cùng cú pháp ::specifier1,specifier2...:
qdl create-zip installer-ufs.zip contents.xml::ufsFile zip tạo ra sau này flash bằng qdl flash installer.zip. Cả việc tạo lẫn flash file zip đều cần bản qdl có hỗ trợ libzip, vốn bật mặc định.
Chạy thử không cần thiết bị (dry run)
Tuỳ chọn --dry-run chạy qdl mà không kết nối hay flash thiết bị nào. Cách này hữu ích để kiểm tra file XML descriptor và tham số programmer, hoặc để sinh bảng digest cho VIP:
qdl --dry-run prog_firehose_ddr.elf rawprogram*.xml patch*.xmlĐọc và ghi binary trực tiếp
Ngoài việc flash theo mô tả XML, qdl còn đọc ghi binary trực tiếp:
qdl prog_firehose_ddr.elf [read | write] [address specifier] <binary>...Có thể đưa nhiều lệnh read và write trong cùng một lần chạy. Address specifier nhận các dạng sau:
| Dạng | Ý nghĩa |
|---|---|
N | Physical partition số N, bắt đầu từ sector 0. Khi đọc, phải nêu rõ số sector bằng dạng N/S+L |
N/S | Physical partition số N, bắt đầu từ sector S. Khi đọc, phải nêu rõ số sector bằng dạng N/S+L |
N/S+L | Physical partition số N, sector bắt đầu S, và số sector L sẽ được ghi vào hoặc đọc ra |
partition name | Một chuỗi, so khớp với tên partition trong các bảng GPT trên mọi physical partition |
N/partition_name | Một số rồi tới một chuỗi, so khớp tên partition trong bảng GPT của physical partition N |
Validated Image Programming (VIP)
qdl hỗ trợ chế độ VIP, chế độ này được kích hoạt khi target bật Secure Boot. VIP kiểm soát những packet nào được phép gửi tới target: nó băm toàn bộ dữ liệu nhận được rồi so từng digest với mục kế tiếp trong một bảng digest đã nạp sẵn. Digest khớp thì packet được chấp nhận; không khớp thì packet bị từ chối và target dừng lại.
Muốn dùng VIP, phải sinh bảng digest trước khi flash. Chạy qdl với tuỳ chọn --create-digests kèm đường dẫn lưu bảng VIP. Lưu ý --create-digests tự bật luôn chế độ dry-run, nên không cần kết nối thiết bị:
mkdir vipqdl --create-digests=./vip prog_firehose_ddr.elf rawprogram*.xml patch*.xmlKết quả sinh ra ba loại file:
DIGEST_TABLE.bin— chứa bảng digest SHA256 của mọi Firehose packet sẽ gửi tới target. Đây là bảng trung gian, chỉ dùng để sinh tiếpDigestsToSign.binvà các fileChainedTableOfDigests<n>.bin, bản thân qdl không dùng trực tiếp khi flash VIP.DigestsToSign.bin— gồm 53 digest đầu tiên cộng với hash SHA256 củaChainedTableOfDigests0.bin. File này phải được chuyển sang định dạng MBN rồi ký bằngsectools:
sectools mbn-tool generate --data DigestsToSign.bin --mbn-version 6 --outfile DigestsToSign.bin.mbnsectools secure-image --sign DigestsToSign.bin.mbn --image-id=VIPXem security profile của SoC đang dùng để biết cần phiên bản định dạng MBN nào.
ChainedTableOfDigests<n>.bin— chứa phần digest còn lại, chia thành nhiều file, mỗi file tối đa 255 digest. File nào chưa phải cuối cùng thì được nối thêm hash SHA256 của bảng chained kế tiếp. File cuối cùng được nối thêm một byte 0 để kích thước không phải bội số của sector size.
Khi flash board ở chế độ VIP, chỉ ra nơi lưu các bảng đã sinh và đã ký bằng --vip-table-path:
qdl --vip-table-path=./vip prog_firehose_ddr.elf rawprogram*.xml patch*.xmlHai tuỳ chọn --vip-table-path và --create-digests loại trừ nhau, không dùng cùng lúc được.
Kiểm tra bảng VIP khi chưa có phần cứng
Trước khi flash thiết bị thật, có thể kiểm tra xem bảng digest đã ký có khớp với dữ liệu sắp gửi hay không, bằng cách dùng --dry-run cùng --vip-table-path:
qdl --dry-run --vip-table-path=./vip prog_firehose_ddr.elf rawprogram*.xml patch*.xmlqdl sẽ mô phỏng trọn phiên Firehose, tính SHA256 cho từng packet mà nó định gửi, rồi so từng hash với mục tương ứng trong bảng digest đã nạp. Mọi chỗ lệch đều được báo ra stderr, và tất cả lỗi được in hết trước khi thoát để nhìn thấy toàn bộ vấn đề cùng lúc.
Cách này phát hiện sớm tình trạng bảng và dữ liệu lệch nhau, trước khi bắt tay vào flash thật, nên hợp cả khi kiểm tra tại máy lẫn khi đưa vào pipeline CI.
Target cần nhiều programmer
Một số target cần nạp nhiều file mới vào được Firehose programmer; những target này yêu cầu nhiều image qua Sahara. qdl có ba cách cung cấp các image đó.
Cách 1: tham số dòng lệnh. Tham số programmer nhận một danh sách các cặp “id” và “filename”, cặp ngăn nhau bằng dấu phẩy, còn id và filename ngăn nhau bằng dấu hai chấm. Mỗi filename ứng với Sahara image của id tương ứng:
qdl 13:prog_firehose_ddr.elf,42:the-answer rawprogram.xmlCách 2: file XML cấu hình Sahara. Các bản META dạng flattened có sẵn các image cần nạp để vào Firehose mode, kèm một file XML sahara_config định nghĩa Sahara image id cho từng image. Nếu programmer được chỉ định là một file XML cấu hình Sahara, qdl sẽ phân tích file đó rồi nạp và phục vụ các file được tham chiếu khi thiết bị yêu cầu:
qdl sahara_programmer.xml rawprogram.xmlCách 3: programmer archive. Việc liệt kê tay danh sách id và filename vừa rườm rà vừa dễ sai, nên qdl chấp nhận một “programmer archive” để dùng công cụ y như với target chỉ cần một programmer. Archive này chứa các Sahara image, mỗi image gắn với Sahara id mà target cần.
qdl tạo được archive từ chính các dạng mô tả programmer mà nó nhận khi flash. Từ danh sách Sahara image trên dòng lệnh:
qdl create-sahara-archive programmer.bin 13:prog_firehose_ddr.elf,42:the-answerTừ file XML cấu hình Sahara:
qdl create-sahara-archive programmer.bin sahara_programmer.xmlTừ file contents XML:
qdl create-sahara-archive programmer.bin contents.xmlKhi contents XML mô tả nhiều tổ hợp storage hoặc flavor, chọn đúng một tổ hợp bằng cú pháp ::specifier giống như với flash:
qdl create-sahara-archive programmer.bin contents.xml::ufsSau đó truyền programmer.bin cho qdl, các image bên trong sẽ được phục vụ theo thứ tự để vào Firehose mode.
Thu thập crash dump
Khi một target Qualcomm bị crash hoặc bị ép vào chế độ crash dump, bootloader sẽ liệt kê lại thiết bị qua USB với Product ID 900e và cung cấp các vùng nhớ để thu thập qua giao thức Sahara.
Có thể kích hoạt một kernel crash trên target bằng:
echo c > /proc/sysrq-triggerTrên máy host, dùng qdl ramdump để thu thập dump:
qdl ramdump -o ./ramdumpMỗi vùng nhớ được cung cấp sẽ ghi thành một file riêng trong ./ramdump. Muốn lấy một số vùng nhất định, truyền thêm bộ lọc ngăn nhau bằng dấu phẩy:
qdl ramdump -o ./ramdump OCIMEM,CODERAMSahara kickstart cho thiết bị flashless (qdl ks)
Lệnh con qdl ks (kickstart) dùng giao thức Sahara để nạp image từ host xuống thiết bị. Nó nhắm tới các thiết bị boot kiểu flashless như Qualcomm Cloud AI 100, loại lấy firmware từ host mỗi lần boot thay vì lưu sẵn trên thiết bị.
Khác với flash thông thường, kickstart dừng lại ngay khi Sahara xong: không nạp Firehose programmer và không ghi gì vào storage.
Bắt buộc có một tham số: -s id:path để đăng ký một ánh xạ image. Tham số này lặp lại được, mỗi dòng cho một Sahara image id mà thiết bị có thể yêu cầu.
Mặc định thiết bị được tìm qua đúng các backend mà những lệnh con khác dùng, nên --backend và --serial chọn thiết bị y như khi flash:
qdl ks -s 13:prog_firehose_ddr.elfVới thiết bị được kernel driver phơi ra dưới dạng một node, chẳng hạn các Sahara endpoint của MHI, dùng -p. Tuỳ chọn này làm việc bằng các thao tác open/read/write thông thường chứ không qua backend:
qdl ks -p /dev/mhi0_QAIC_SAHARA \ -s 1:/opt/qti-aic/firmware/fw1.bin \ -s 2:/opt/qti-aic/firmware/fw2.binVì -p đã chỉ thẳng transport nên không dùng chung được với --serial hay --backend.
Các file được ánh xạ không cần tồn tại ngay lúc chạy lệnh. Nếu qdl ks không mở được file được yêu cầu, thiết bị sẽ tự quyết định bước tiếp theo. Nhờ vậy có thể gắn qdl ks vào một udev rule duy nhất phục vụ nhiều cấu hình thiết bị, ví dụ một image huấn luyện DDR tuỳ chọn chỉ có mặt trên vài hệ thống.
Flash từ WSL2 bằng usbipd-win
Trên Windows, qdl chạy được bên trong một bản phân phối WSL2, nhưng WSL2 mặc định không thấy thiết bị USB: phải chuyển tiếp thiết bị từ phía Windows bằng usbipd-win. Cài nó ở phía Windows (winget install usbipd), rồi chuyển tiếp thiết bị EDL vào WSL.
Thiết bị EDL xuất hiện với Vendor ID 05c6 và Product ID 9008 (Firehose), 900e (crash dump), 90db (diag dump) hoặc 901d. Từ terminal Windows, liệt kê thiết bị đang cắm và ghi lại BUSID của thiết bị EDL:
usbipd listMỗi thiết bị phải được bind một lần trước khi attach. Bước bind cần PowerShell chạy quyền Administrator, nhưng nó tồn tại qua các lần khởi động lại, nên mỗi thiết bị chỉ làm một lần:
usbipd bind --busid <BUSID>Bước attach thì không cần quyền Administrator và gọi được từ trong WSL bằng cách chạy file thực thi phía Windows:
usbipd.exe attach --wsl --busid <BUSID>Attach xong, thiết bị xuất hiện trong WSL (kiểm tra bằng lsusb, tìm 05c6:9008) và qdl flash được như bình thường.
Attach lại sau khi EDL liệt kê lại
Thiết bị EDL là một USB gadget chỉ tồn tại khi board đang ở EDL mode. Mỗi lần board vào lại EDL, kể cả sau khi qdl chạy xong và target khởi động lại, thiết bị USB bị huỷ rồi tạo lại. Bind vẫn còn, nhưng attach thì mất, và host có thể gán BUSID khác lần trước. Một lần chạy qdl báo không tìm thấy thiết bị hầu như luôn là do chuyện này: chỉ cần attach lại.
Sau mỗi lần vào EDL, dò lại BUSID rồi attach lần nữa. Việc này viết thành script chạy từ WSL được, khỏi phải tra BUSID bằng tay:
BUSID=$(usbipd.exe list | grep -iE '9008|900e|901d|90db' | awk '{print $1}' | tr -d '\r')usbipd.exe attach --wsl --busid "$BUSID"Nếu flash nhiều lần liên tiếp, chạy bước attach lại này trước mỗi lần gọi qdl.
nbdkit plugin
Ngoài chương trình qdl, còn build được một nbdkit plugin. Plugin này nạp firehose programmer rồi phơi một physical partition (LUN) ra host dưới dạng block device, nhờ vậy sửa được bảng phân vùng và mount được các partition bằng công cụ thông thường. Xem docs/nbd.md để biết cách build và ví dụ sử dụng.
Chạy test và sinh man page
Chạy bộ integration test bằng meson với tham số test:
meson test -C buildNếu lúc configure có sẵn cmocka, Meson cũng build và chạy bộ unit test, trong đó có các test về đường dẫn của program_load_xml. Chạy riêng unit test bằng:
meson test -C build --suite unitSinh man page bằng target manpages:
meson compile manpages -C buildTham khảo
- linux-msm/qdl trên GitHub — README, đối chiếu ngày 23/09/2026 (v2.8).
- CONTRIBUTING.md — coding style, checkpatch, markdown-lint và cách gửi pull request.
- Qualcomm EDL mode — giới thiệu chung về EDL.










