Video failed to load

qdl: Flashing over EDL Mode

2338 words
12 minutes
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#

Terminal window
sudo apt install libxml2-dev libusb-1.0-0-dev libzip-dev meson ninja-build help2man
meson setup build
meson compile -C build

macOS#

Với Homebrew:

Terminal window
brew install libxml2 libusb libzip meson ninja help2man
meson setup build
meson compile -C build

Với MacPorts:

Terminal window
sudo port install libxml2 libusb libzip meson ninja help2man
meson setup build
meson compile -C build

Windows#

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:

Terminal window
pacman -S base-devel --needed
pacman -S git
pacman -S help2man
pacman -S mingw-w64-x86_64-gcc
pacman -S mingw-w64-x86_64-meson
pacman -S mingw-w64-x86_64-ninja
pacman -S mingw-w64-x86_64-libusb
pacman -S mingw-w64-x86_64-libxml2
pacman -S mingw-w64-x86_64-libzip

Rồi build bằng meson như trên:

Terminal window
meson setup build
meson compile -C build

Tuỳ 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:

Terminal window
meson setup build -Dzip-container=disabled

Flash cơ bản#

Chạy qdl --help để xem đầy đủ cách dùng. Ví dụ flash một bản build dạng FLAT:

Terminal window
qdl prog_firehose_ddr.elf rawprogram*.xml patch*.xml

Nếu máy host đang cắm nhiều board, chỉ định số serial của board cần flash bằng --serial:

Terminal window
qdl --serial=0AA94EFD prog_firehose_ddr.elf rawprogram*.xml patch*.xml

Flash 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:

Terminal window
qdl flash <installer.zip>

Nếu package đã giải nén:

Terminal window
qdl flash flashmap.json

Cả 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...]:

Terminal window
qdl flash installer.zip::layout1/ufs

Flash từ contents.xml#

qdl cũng flash được các bản build mô tả bằng file contents.xml:

Terminal window
qdl flash contents.xml

Vì 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ụ:

Terminal window
qdl flash contents.xml::ufs,safe_rtos

Lệ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:

Terminal window
qdl create-zip installer.zip contents.xml

Khi 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...:

Terminal window
qdl create-zip installer-ufs.zip contents.xml::ufs

File 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:

Terminal window
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:

Terminal window
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:

Bảng 1: Các dạng address specifier
DạngÝ nghĩa
NPhysical 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/SPhysical 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+LPhysical partition số N, sector bắt đầu S, và số sector L sẽ được ghi vào hoặc đọc ra
partition nameMộ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_nameMộ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ị:

Terminal window
mkdir vip
qdl --create-digests=./vip prog_firehose_ddr.elf rawprogram*.xml patch*.xml

Kế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ếp DigestsToSign.bin và các file ChainedTableOfDigests<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ủa ChainedTableOfDigests0.bin. File này phải được chuyển sang định dạng MBN rồi ký bằng sectools:
Terminal window
sectools mbn-tool generate --data DigestsToSign.bin --mbn-version 6 --outfile DigestsToSign.bin.mbn
sectools secure-image --sign DigestsToSign.bin.mbn --image-id=VIP

Xem 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:

Terminal window
qdl --vip-table-path=./vip prog_firehose_ddr.elf rawprogram*.xml patch*.xml

Hai 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:

Terminal window
qdl --dry-run --vip-table-path=./vip prog_firehose_ddr.elf rawprogram*.xml patch*.xml

qdl 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:

Terminal window
qdl 13:prog_firehose_ddr.elf,42:the-answer rawprogram.xml

Cá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:

Terminal window
qdl sahara_programmer.xml rawprogram.xml

Cá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:

Terminal window
qdl create-sahara-archive programmer.bin 13:prog_firehose_ddr.elf,42:the-answer

Từ file XML cấu hình Sahara:

Terminal window
qdl create-sahara-archive programmer.bin sahara_programmer.xml

Từ file contents XML:

Terminal window
qdl create-sahara-archive programmer.bin contents.xml

Khi 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:

Terminal window
qdl create-sahara-archive programmer.bin contents.xml::ufs

Sau đó 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:

Terminal window
echo c > /proc/sysrq-trigger

Trên máy host, dùng qdl ramdump để thu thập dump:

Terminal window
qdl ramdump -o ./ramdump

Mỗ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:

Terminal window
qdl ramdump -o ./ramdump OCIMEM,CODERAM

Sahara 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:

Terminal window
qdl ks -s 13:prog_firehose_ddr.elf

Vớ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:

Terminal window
qdl ks -p /dev/mhi0_QAIC_SAHARA \
-s 1:/opt/qti-aic/firmware/fw1.bin \
-s 2:/opt/qti-aic/firmware/fw2.bin

Vì -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#

Warning

Cách này hiện chưa ổn định với usbipd-win. Xem các issue #924, #1022 và #1067.

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:

Terminal window
usbipd list

Mỗ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:

Terminal window
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:

Terminal window
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:

Terminal window
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:

Terminal window
meson test -C build

Nế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:

Terminal window
meson test -C build --suite unit

Sinh man page bằng target manpages:

Terminal window
meson compile manpages -C build

Tham khảo#

  1. linux-msm/qdl trên GitHub — README, đối chiếu ngày 23/09/2026 (v2.8).
  2. CONTRIBUTING.md — coding style, checkpatch, markdown-lint và cách gửi pull request.
  3. Qualcomm EDL mode — giới thiệu chung về EDL.

Comments

Profile Image of the Author
Art
When the world stopped, I started compiling.
Announcement
Welcome to my blog!
Latest Moments
Table of Contents