493 Commits
Author SHA1 Message Date
Igor Opaniuk 1dcbdd0e6d Merge pull request #289 from igoropaniuk/refactor/edl-interface-identity
qdl: drop the EDL product id allowlist and match the EDL interface signature when enumerating
v2.8-rc1
2026-07-23 11:20:46 +02:00
Igor Opaniuk 7c5a28d7dd Merge pull request #291 from igoropaniuk/fix/windows-test-warnings
Address oscompat warnings on Windows
2026-07-21 15:02:08 +02:00
Igor Opaniuk fd0e86d542 tests: warn and skip overly-long paths in rmtree
rmtree() concatenates the directory path and entry name into a
PATH_MAX buffer without checking for truncation, which -Wformat-
truncation flags on the Windows build where PATH_MAX is 260. A
truncated path would also make the cleanup delete the wrong file in
principle.

Check the snprintf() result and skip entries that do not fit; using
the return value also satisfies the warning. A skipped entry means
the parent rmdir() fails with ENOTEMPTY and the workdir lingers, so
report the skip on stderr instead of hiding it - the message names
the entry, making a leftover workdir traceable to its cause.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-21 14:57:42 +02:00
Igor Opaniuk e0ad540d5a Merge pull request #292 from igoropaniuk/chore/markdownlint-all
Lint all markdown files and extend the release documentation
2026-07-21 14:23:22 +02:00
Igor Opaniuk 89bfed3976 README: replace non-ASCII punctuation
The WSL2 section carries a typographic apostrophe and em dashes;
project documentation is plain ASCII (see AGENTS.md). Replace them
with their ASCII equivalents.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-21 14:03:24 +02:00
Igor Opaniuk dd6ab15ec9 doc: document stable branch maintenance in RELEASING.md
Describe the kernel-style stable branches (qdl-X.Y.y) agreed for
shipping urgent fixes independently of master: cut the branch from
the release tag immediately after tagging, backport fixes that landed
on master with cherry-pick -x through stable-targeted pull requests
(CI runs on those unchanged), and cut point releases (vX.Y.Z) from
the branch with the same flow as a normal release.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-21 14:03:24 +02:00
Igor Opaniuk eca982a599 scripts: lint every tracked markdown file
The markdown lint wrapper names README.md and docs/ explicitly, so
documents like AGENTS.md, CONTRIBUTING.md and RELEASING.md have never
been linted and accumulated findings silently.

Derive the file list from git ls-files instead, excluding
CHANGELOG.md - generated by the release process, where long
release-note URLs and per-release repeated section headers are part
of the format - and CLAUDE.md, a symlink to the already-linted
AGENTS.md. New documents are picked up automatically.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-21 14:03:24 +02:00
Igor Opaniuk 6c47f1cb7c doc: fix markdown lint findings in AGENTS.md and RELEASING.md
Preparation for linting every markdown file in the tree: surround the
sign-off example in AGENTS.md with the blank lines the linter wants,
and flatten the RELEASING.md table of contents - its single top-level
entry only repeated the document title, and the nested indentation
tripped the linter's list-indent rule under kramdown.

While at it, replace the literal Signed-off-by example with the
'git commit -s' command that produces it. checkpatch validates
anything shaped like a signature tag on any patch line - added,
removed and context lines alike - so any patch touching these lines
trips 'Unrecognized email address' on the $(git config ...)
placeholders. Documenting the command instead keeps trailer-shaped
text out of the file entirely, with no checkpatch exceptions needed.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-21 14:03:19 +02:00
Igor Opaniuk 760d15b4d0 oscompat: harden path_is_absolute against empty strings
The Windows branch reads path[1] guarded only by short-circuit
evaluation, which is correct at runtime but trips -Warray-bounds when
the function is inlined with a string literal shorter than two bytes,
as the oscompat unit tests do with "". It also passes a plain char
to isalpha(), which is undefined behavior for negative values.

Return early for the empty string, so the path[1] access is provably
in bounds, and feed isalpha() an unsigned char.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-18 09:18:37 +02:00
Igor Opaniuk a3fa75b761 vip: open signed tables in binary mode on Windows
The signed VIP table (DigestsToSign.bin.mbn) and the chained digest
tables are opened in vip_transfer_init() with a plain O_RDONLY. On
Windows/MSYS2 a descriptor opened without O_BINARY defaults to text
mode, which performs CRLF translation and treats a 0x1A (Ctrl-Z) byte
as end-of-file.

These tables are arbitrary binary data (e.g. the 8192-byte signed
table). As soon as read() hits a 0x1A byte or collapses a CRLF pair it
returns fewer bytes than the file size, so vip_transfer_send_raw() fails
its n == sb.st_size length check and aborts with "failed to read
binary". The VIP configure request then fails. On Linux there is no
text/binary distinction (O_BINARY is defined as 0) so the same code
works fine.

Add O_BINARY to both open() calls in vip_transfer_init(), matching what
write_digests_to_table() already does, so the tables are read verbatim
on Windows as well.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-17 17:29:15 -05:00
Igor Opaniuk 4c300a243c qdl: drop the EDL product id allowlist
EDL, crash-mode and ramdump devices enumerate with a growing set of
product ids, and every newly discovered variant needed another
allowlist entry - devices with ids not yet on the list were silently
invisible until someone updated it. Both backends now identify the
EDL function by the vendor-specific interface signature instead: the
libusb backend via the config descriptors, the QUD backend via the
Windows compatible ids derived from them.

Trust the vendor id alone in qdl_is_edl_device() and drop the
now-redundant pid gate in the QUD enumeration. The pid parameter
stays in the signature so call sites are unchanged; the interface
gates are what reject non-EDL Qualcomm devices from here on.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-17 16:21:49 +02:00
Igor Opaniuk 52ae194b09 qud: match the EDL interface signature when enumerating
The QUD backend accepts any Qualcomm COM port whose pid is on the
shared EDL allowlist, while the libusb backend identifies the EDL
function by its interface signature - vendor-specific class and
subclass with a known Sahara protocol. Windows exposes exactly that
triple without opening the device: PnP derives compatible ids of the
form "USB\Class_xx&SubClass_xx&Prot_xx" for every USB devnode,
including each interface of a composite device.

Require the same signature set the libusb backend matches (0x10, 0x11
and 0x13 on modern devices, 0xff on older targets), bringing the two
backends' notion of an EDL device in line and filtering Qualcomm COM
ports that expose no vendor-specific function at all.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-17 16:21:49 +02:00
Igor Opaniuk f312075fee Merge pull request #283 from igoropaniuk/feat/nbdkit
nbdkit: expose a device LUN as a block device
2026-07-17 09:48:14 +02:00
Bjorn Andersson 23688db35a doc: document the nbdkit plugin
The nbdkit plugin is undiscoverable without documentation. Describe how to
build it with meson (including the nbdkit development dependency) and walk
through serving a LUN end to end: starting nbdkit, confirming the export
with nbdinfo, attaching it to /dev/nbdN with nbd-client, and reading it,
plus a warning that writes go straight to the device's flash.

The walkthrough lives in docs/nbd.md to keep the ever growing README lean;
the README gets a short section pointing to it, and the markdown-lint
wrapper now lints everything under docs/ so future documents are covered
automatically.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-17 09:42:05 +02:00
Bjorn Andersson 16ca90ba9a nbdkit: add qdl plugin exposing a LUN as a block device
Inspecting or editing a device's flash previously meant dumping partitions
to files and reflashing them; there was no way to treat the live storage as
a random-access block device. Exposing a LUN over nbdkit lets ordinary host
tools - parted, mount, dd, imaging tools - operate on it directly.

Add an nbdkit plugin that uploads the firehose programmer and serves a
physical partition (LUN) as a read/write block device:

$ modprobe nbd
$ nbdkit -fv ./nbdkit-qdl-plugin.so programmer=prog_firehose_ddr.elf lun=0

meson builds it only when the nbdkit dependency is present (pass
-Dnbdkit=enabled to require it), producing nbdkit-qdl-plugin.so from the
shared qdl sources; the qdl binary is unaffected.

An EDL device can only be programmed once per session - uploading the
programmer is a one-shot transition and a reset reboots it out of firehose
- but nbdkit calls .open/.close several times per client (NBD_OPT_INFO to
probe, then NBD_OPT_GO to transfer). Programming per open and resetting per
close therefore made the first close reboot the board and every later open
time out. To solve this the plugin sets the device up once, on the first
open, keeps it configured across connections, and resets it only when the
plugin unloads.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-17 09:36:28 +02:00
Igor Opaniuk fa4872777d qdl: move decode_sahara_config into its own library unit
decode_sahara_config() was defined in qdl.c - the command-line front-end -
but it is also called from contents.c, which is part of the shared qdl
library. The qdl binary links both, so it built and ran fine; but any
program that links the library without qdl.c ends up with an undefined
reference to decode_sahara_config().

That is exactly what happens to the nbdkit plugin, which is built from
the shared sources plus its own entry point and no qdl.c: loading the
resulting shared object aborts with "undefined symbol:
decode_sahara_config".

Move the function into a dedicated src/sahara_config.c that is part of
the shared source set, so the library is self-contained. It is kept in
its own translation unit rather than folded into contents.c so that the
contents unit tests, which #include contents.c and stub this function,
keep working.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-17 09:36:28 +02:00
Bjorn Andersson a8f21545f0 firehose: add block-level read/write/size interface
The firehose layer only drives file-oriented flashing: <program>/<read>
operations described by XML and executed as an op list. Nothing let an
in-process consumer configure the programmer on its own, learn how large a
LUN is, or read and write arbitrary sector ranges to and from memory
buffers.

The upcoming nbdkit plugin needs exactly that - random-access block I/O
against a live device - so add the missing primitives:

  - firehose_open()    configure the programmer without running any ops
  - firehose_getsize() report a LUN's sector size and count, parsed from
                       the <getstorageinfo> JSON response
  - firehose_pread()   read a sector range into a buffer, reusing the
                       chunked read path
  - firehose_pwrite()  write a buffer to a sector range, streaming it in
                       max_payload_size chunks
  - firehose_reset()   made non-static, out-of-tree callers can reuse it

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-17 09:34:14 +02:00
Igor Opaniuk 81a061df4a github: allow 75-character commit body lines
Kernel-style commit bodies occasionally wrap a sentence or an
example command a few characters past 72, which tripped
commitlint's body-max-line-length even though the messages are
otherwise fine. Raise the body limit from 72 to 75 to match the
wrapping already used in the tree.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-17 09:34:14 +02:00
Yvonne Kaire f9f953bb74 usb: accept bInterfaceProtocol 0x13 for crash-mode devices
Sahara is identified by bInterfaceProtocol 0x10, 0x11, or 0x13 on
modern devices, and 0xff on older targets. Add 0x13 to the accepted
protocols and use hex notation consistently.

Signed-off-by: Yvonne Kaire <ykaire@qti.qualcomm.com>
2026-07-16 21:15:08 -05:00
Igor Opaniuk 4a96950c03 qdl: reject mixing input XML files with command verbs
The usage synopsis documents input XML files and the command verbs
(read/write/erase/sha256/flash/reset) as separate invocation forms,
but the argument loop accepted any mixture of them. Combining a bare
program XML with the reset verb, for example, appended the reset op
during parsing - before qdl_determine_bootable() adds the
setbootablestoragedrive op after the loop - so the device could be
told to reboot before its bootable storage drive was set. The same
mixture on the provisioning path silently performed no reset at all.

Rather than order the two against each other, reject the combination:
track whether a file-type and a verb-type argument have both been
seen and error out when they have. This keeps each documented form
self-consistent and makes the reset verb usable only on its own or
alongside other verbs, where ordering is well defined.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-16 17:29:18 -05:00
Igor Opaniuk cb5331ac72 firehose: harden response, geometry and retry handling
Several long-standing robustness gaps in the Firehose layer:

- firehose_write() keyed its timeout retry on errno, but the qdl_write()
  backends report the timeout through the return value and never touch
  errno, so the retry could misfire and a stale errno leaked into the
  return code. Key both the retry and the error return on the return
  value, and free the serialized document on the VIP send-table error
  path.

- firehose_configure_response_parser() leaked the response value on the
  ACK path when MaxPayloadSizeToTargetInBytesSupported was absent; free
  it.

- firehose_program() and firehose_issue_read() divided by a sector size
  that could be zero; guard against it and fail cleanly. Route the
  VIP send-table failure through the existing cleanup path instead of
  returning with the document and buffer still allocated.

- firehose_detect_and_configure() ignored its skip_storage_init argument
  and always passed false; honor the caller's request.

- Normalize a few error returns to -1 so callers see a consistent
  failure status.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-16 17:28:34 -05:00
Igor Opaniuk b2e63e2cdd firehose: reserve a byte for the NUL terminator on a full read
firehose_read() read up to sizeof(buf) bytes and then wrote a NUL at
buf[n], overflowing the stack buffer by one byte whenever a read filled
it - routine on stream transports that concatenate responses. Read at
most sizeof(buf) - 1 bytes.

Fixes: da68b4add8 ("qdl: Support for program and patch")
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-16 17:28:34 -05:00
Igor Opaniuk c3f4389f50 firehose: report a failed final programming response
firehose_program() logged a failed or NAK'd final programming response
but then returned 0, so firehose_execute_ops() carried on and qdl
exited successfully with the partition left unwritten. Return an error
on a non-ACK final response.

Fixes: a9ec681705 ("program: Refactor the global "programs" list")
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-16 17:28:34 -05:00
Igor Opaniuk 8fc29dec5b sahara: fix ramdump crash and harden device-driven parsing
qdl ramdump passes a NULL images array, but the READ_DATA handlers and
the done-pending quirk indexed it unconditionally and crashed when the
device requested an image. Treat NULL images as "none". Also make the
offset+len range check overflow-safe, use a signed type for write()'s
result, propagate I/O errors from the ramdump helper, bound the region
loop by the bytes actually read, and reject region filenames that
contain a path separator.

Fixes: ff1a3bb1d3 ("ramdump: Make ramdump a qdl subcommand")
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-16 17:28:34 -05:00
Igor Opaniuk 637b48f3d0 gpt: use 64-bit sector arithmetic and harden entry parsing
GPT LBAs are 64-bit but were narrowed to unsigned int, so a partition
beyond 2 TB wrapped and a name-addressed write/erase hit the wrong
sectors. Carry start_sector/num_sectors as uint64_t and range-check
before storing num_sectors. Also reject a bad part_entry_size, fix an
off-by-one in the UTF-16 surrogate handling that read past the name
buffer, and initialise the sim-mode outputs.

Fixes: 60db50966d ("qdl: Extend read/write support to accept GPT partition names")
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-07-16 17:28:34 -05:00