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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
The image-id checks used "id == 0", but id comes from strtoul(), so an
id above LONG_MAX parsed negative, passed the check and indexed the
sahara_image array out of bounds.
Reject id <= 0 and also require the cpio entry name to be non-empty
and NUL-terminated before strtok_r()/strcmp(), and treat an empty
image_path as invalid rather than calling strlen(NULL).
Fixes: 4bdcaaec1a ("qdl: Support for "programmer archives"")
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
A program tagged sparse="true" with no sparse header but a matching
size was appended to the op list twice - once in program_load_sparse()
and again by the caller - self-linking the node (infinite loop /
use-after-free) and dropping its filename so it is never flashed.
Return a distinct code so the caller appends it once. Also guard the
sparse path against a zero SECTOR_SIZE_IN_BYTES (SIGFPE) and empty
start_sector/label attributes (NULL deref).
Fixes: 6a67b6bd64 ("sparse: Parse sparse files at load time")
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
flashmap_load() left "ret" holding the 0 from a successful open, so a
failed load or JSON parse returned success and qdl continued with an
empty op list instead of aborting.
Set ret to -1 on both paths, and NULL-check the configure-op allocation.
Fixes: 81b68cbdb2 ("flashmap: Introduce flashmap support")
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
zipper_write() reused "ret" as a loop scratch variable, so several
error paths jumped to the discard label without resetting it to -1:
create-zip then exited 0 with no output file.
Set ret before those gotos, also hand sparse fill buffers straight to
libzip instead of copying them a second time, which could double peak
memory on large fills.
Fixes: cc6d9c3596 ("zipper: Introduce support for creating a flashmap zip file")
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
win_enumerate_qcom() accepted any present COM port whose hardware id
starts with USB\VID_05C6&PID_, i.e. any Qualcomm serial port the
QDLoader driver (or another vendor serial driver) has bound. That
over-matches: the diag or NMEA port of a phone in normal composite
mode also enumerates this way, and would be counted by
qud_probe_present(), listed by 'qdl list' and even opened and driven
by qud_open() as if it were an EDL device.
Gate the enumeration with qdl_is_edl_device(), the same identity
policy the libusb backend applies. The backends can no longer drift
apart on what counts as an EDL device: extending the policy (for
example with a ramdump-specific pid) is a one-line change in qdl.h
that both transports pick up.
Devices whose hardware id carries no parsable pid were previously
listed with pid 0 and are now skipped.
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
usb_open_device() interleaved matching and acting: the interface
signature check, the device open, the serial filter and the claim all
lived in one loop, with the device-level serial check nested inside
the per-interface iteration. A serial mismatch was therefore
discovered only after opening the device, and rediscovered - open,
read, close - for every matching interface.
Introduce usb_match_device(), which answers the complete device-level
question on an opened candidate: does it carry the requested serial,
and does it expose an EDL interface? usb_open_device() becomes a
plain sequence: open, match, detach and claim, fill in the endpoint
pair. The match helpers now form one family: usb_match_device() is
usb_match_serial() (renamed from usb_match_usb_serial() to drop the
stutter) plus usb_match_edl_interface() over the active config.
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
usb_open_once() and usb_list() each carried their own copy of the
same enumeration loop: walk the bus, fetch descriptors, apply the EDL
identity gate. On top of that, usb_open_once() applied the gate
before handing a device to usb_open_device(), which then fetched the
same descriptor and applied the same gate a second time; the
duplicated check let the two layers disagree about what counts as an
EDL candidate.
Extract the loop into usb_enumerate_edl_devices(), which hands every
candidate to a callback and reports how many it saw, and pass the
already-fetched descriptor down to usb_open_device() so candidate
selection happens exactly once, in exactly one place. Only what
happens to a matched candidate now differs between the paths: the
open path claims the first candidate that opens, while the list verb
reads the descriptors of all of them without claiming anything
(listing must never detach a kernel driver or claim an interface, as
that could disturb a concurrently running flash). usb_list() no
longer sizes its result array from the raw device count but grows it
per matched candidate.
The libusb session stays with the callers, since their lifetimes
differ: usb_open_once() must keep the session alive for the handle it
returns, while usb_list() tears it down before returning.
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
usb_list() open-codes the iProduct "_SN:" parsing that
usb_read_serial() already implements for the open path. Reuse the
helper so the serial-number format knowledge exists once.
Two edge cases change deliberately: a device whose iProduct string
cannot be read is now listed with a "(none)" serial instead of
being skipped, and a serial longer than the 16-byte descriptor field
is truncated (as the open path already does) instead of hiding the
device. Suppressing a present device was the more surprising
behavior for a listing verb.
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
usb_open_device() mixes three concerns in one hundred-line function:
deciding whether a USB device is an EDL candidate, locating the
vendor-specific EDL interface with its bulk endpoint pair, and
actually opening and claiming it. usb_open_once() and usb_list()
additionally duplicate the vid/pid gate inline, so the answer to
"what does qdl consider an EDL device" is currently scattered over
three call sites that can (and in the past did) drift apart.
Centralize the policy:
- qdl_is_edl_pid()/qdl_is_edl_device() in qdl.h hold the shared,
transport-agnostic identity check (Qualcomm vid, known EDL pids).
Living in the common header, the same policy is available to the
QUD backend, which parses pids from Windows hardware ids.
- usb_is_edl_device() wraps it for libusb descriptor types and is
now the single gate used by the open, scan and list paths.
- usb_match_edl_interface() checks one interface descriptor for the
ff/ff class/subclass signature and a known protocol, and extracts
the bulk endpoint pair.
usb_open_device() shrinks to the actual open/claim sequence. Future
changes to device acceptance (for example crash-mode devices exposing
a different interface protocol or pid) become a one-line change in
exactly one place, applied consistently everywhere.
No functional change.
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>