238 Commits

Author SHA1 Message Date
Bjorn Andersson
ff592b7402 Merge pull request #161 from igoropaniuk/memleaks_sec_issues
Address memory leaks/mitigate buffer overflows
2026-02-25 08:27:14 -06:00
Igor Opaniuk
d192ab7e1a qdl: fix resource leaks in programmer image decoding
On error, decode_programmer_archive() and decode_sahara_config() leak
both the input blob and any images partially loaded before the failure.

Fix by extending their error paths to call sahara_images_free() and
free the blob, mirroring the cleanup already done on success. Also
introduce sahara_images_free() to consolidate teardown, drop the
misleading 'const' from sahara_image.name, and release sahara_images
in qdl_flash() on all exit paths.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-02-24 11:31:41 +01:00
Igor Opaniuk
38ff7561b5 usb: fix sign-compare warning in usb_read()
The `len` parameter is size_t (unsigned) while `actual` from
libusb_bulk_transfer() is int (signed). Their direct comparison
on line 331 triggers -Wsign-compare: if `actual` were somehow
negative, the implicit conversion to size_t would produce a
large positive value, making the comparison silently wrong.

Fixes: b4030fabe6 ("usb: Fix checkpatch warning about unnecessary parenthesis")
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-02-13 10:43:15 +01:00
Igor Opaniuk
1339843010 util: check malloc return value in load_sahara_image()
malloc() can return NULL on allocation failure. Without a check,
the subsequent read(fd, ptr, len) dereferences a NULL pointer,
causing undefined behavior (typically a segfault).

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-02-13 10:43:15 +01:00
Igor Opaniuk
069b852cdc firehose: replace malloc with alloca for sector probe buffer
The sector probe buffer in firehose_try_configure() is a small,
bounded allocation (max 4096 bytes) used only within this function
scope. Replace malloc() with alloca() to:

- avoid the need for a free() path and NULL-check error handling
- simplify control flow, as the buffer is automatically released
  on function return

The upper bound is compile-time constant (largest entry in
sector_sizes[]), well within safe stack allocation limits.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-02-13 10:43:10 +01:00
Igor Opaniuk
9254e0d5c1 gpt: mitigate buffer overflow risk for lba buffer
The `part_entry_lba` field in `struct gpt_header` is uint64_t, but
the local `lba` variable was declared as `unsigned int`, silently
truncating values above UINT_MAX. The buffer `lba_buf[10]` is also
too small: a 64-bit value can be up to 20 decimal digits, requiring
at least 21 bytes including the null terminator. Combined with an
unchecked sprintf(), this is a stack buffer overflow for any LBA
value exceeding 9 digits.

Fix by:
- declaring `lba` as uint64_t to match the source type
- increasing lba_buf to 21 bytes
- using snprintf() with sizeof to prevent overruns
- using PRIu64 for correct format specifier

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2026-02-13 10:11:53 +01:00
Casey Connolly
f1fc7adbb5 firehose: ensure we properly set skip_storage_init in provision
This got broken during some refactoring, pass the correct values through.

Fixes: 841a0a8e7f ("firehose: Improve startup time significantly")
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
2026-02-10 17:48:33 -06:00
Bjorn Andersson
7fd466c95e qdl: Propagate the success of decode_sahara_config()
Commit '44e7be00aca0 ("sahara: Drop "single image" concept")' cleaned up
the handling of programmer selection, but in the new flow failed to
propagate the successful decoding of the sahara_config.

Fixes: 44e7be00ac ("sahara: Drop "single image" concept")
Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2026-02-04 14:37:38 -06:00
Bjorn Andersson
f60f2bde70 qdl: Refactor main()
Rather than mixing subcommands and the default "flash" operation, split
the flashing mechanism out into its own subcommand function to clean up
the main function.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2026-02-02 12:23:24 -06:00
Bjorn Andersson
156d9673a2 ramdump: Provide some user feedback
Dumping all the DDR takes significant time and there's currently no
user-visible feedback provided to indicate that even the process has
started.

Solve this by wiring up the ux module and provide a progress bar while
dumping segments, as well as information as the segments are skipped or
dumped.

Add missing ux_init() to the ramdump setup, and make sure to clamp value
to max in the progress calculation, to avoid funky issues when progress
is made beyond the size of the chunk.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2026-02-02 12:23:24 -06:00
Bjorn Andersson
ff1a3bb1d3 ramdump: Make ramdump a qdl subcommand
Providing qdl-ramdump as a separate executable has resulted in it not
being part of several of the distributions that include qdl. Also, users
that knows that qdl supports ramdumps are looking for it "in" qdl, not a
related tool.

Make "ramdump" a subcommand, just like "qdl list", to improve the
ergonomics of the tool.

The implementation is a (almost) verbatim copy of ramdump.c. The
existing executable is kept in order to not break those distributions
that explicitly do package said executable.
At some point we should figure out how to drop this.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2026-02-02 12:23:24 -06:00
Bjorn Andersson
2d8ea742a8 sahara: Improve error message about missing images
It might not be obvious to the user that an "invalid image id" is the
result of them not providing the correct programmer loaders.

Make the error message more user friendly.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2026-02-02 09:44:25 -06:00
Bjorn Andersson
44e7be00ac sahara: Drop "single image" concept
The Sahara implementation was written without understanding of the
special meaning of image id #13. As the implementation grew support for
multiple images the special casing of "single image" was introduced, and
this spread to the calling bodies.

Prior to the introduction of multi-programmer platforms this didn't
matter, the logic was fairly simple and usage was straight forward.

But when a single programmer image is provided on a multi-programmer
target "single_image" is true and hence the image id is ignored on the
first read, the one provided file is loaded. The typical outcome is that
the following SAHARA_END_OF_IMAGE_CMD fails with a message stating
"non-successful end-of-image result".

Few users draws the conclusion that this is because they didn't provide
the appropriate programmers.

But 13 is the image id for the programmer, so it should be fine to drop
the special logic. This results in a (somewhat) more helpful error
message telling the user that an invalid image is being requested.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2026-02-02 09:44:25 -06:00
lucarin91
f32f5ebe9f usb: add support for listing devices
When working with multiple devices and the --serial argument, the serial
numbers must be known.

Add a new command "qdl list" to list the connected devices in EDL mode
and their serial number.

Signed-off-by: lucarin91 <lucarin@protonmail.com>
[bjorn: Replaced --list with list, changed output to only print serial, some stylistic changes]
Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2026-02-02 09:02:58 -06:00
Bjorn Andersson
32f0d67c02 firehose: Increase timeout for read of program-ack
Commit 'a10cf7f5da7a ("firehose: Increase image write timeout to 60
seconds")' bumped the timeout for writes in the program operation, per
observed behavior. This was confirmed to solve the problems seen when
programming SPINOR in Nord devices

New reports (and measurements) shows the write taking a negligible
amount of time and the read of the following "program ack" taking the
whole reported ~35 second, resulting in a timeout.

The proprietary tools has a write timeout of 60 seconds in some
operating systems and no timeout for others, and a read timeout of 120
seconds.

Update this timeout for the "program ack" to 120 seconds, to match the
proprietary tools.

Fixes: a10cf7f5da ("firehose: Increase image write timeout to 60 seconds")
Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2026-02-02 08:33:18 -06:00
Yvonne Kaire
eb345dfdfa vip: Update expected digest checksum
The expected digest value changed after recent updates to the
generated image content. The hardcoded checksum no longer matched
the artifact produced by the current build, causing verification
to fail.

Update the expected digest so checksum verification succeeds with
the latest generated image.

Signed-off-by: Yvonne Kaire <ykaire@qti.qualcomm.com>
2026-01-29 15:56:01 -06:00
Yvonne Kaire
4fb0de944a firehose: Increase reset delay after flashing
On RB3 and IQ9, devices may intermittently hang if reboot is issued too
soon after flashing, sometimes requiring a full power cycle to recover.
During testing, shorter post-flash delays were more likely to reproduce
the issue. Add a conservative 10s delay between flash completion and
reboot to provide a stabilization window.

This mirrors long-standing behavior in fh_loader and PCAT.

Results (30 runs each, Rubik Pi/IQ8/IQ9):
- QDL 2.4 baseline: 18/30 flash attempts failed
- QDL 2.4 + this change: 30/30 passes
Signed-off-by: Yvonne Kaire <ykaire@qti.qualcomm.com>
2026-01-29 15:56:01 -06:00
Bjorn Andersson
f5b6ce9815 sahara: Unbreak ramdump by making images optional again
Commit 'ff260604e774 ("sahara: Load programmer at parse time")' added a
convenient debug function to dump the list of loaded images, but failed
to recognize that when this is invoked from qdl-ramdump `images` will be
NULL, and hence we have a segfault.

Make the debug call conditional on the presence of images.

Fixes: ff260604e7 ("sahara: Load programmer at parse time")
Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2026-01-28 22:27:00 -06:00
Bjorn Andersson
aa77dfc23e qdl: Allow absolute paths in Windows again
Commit '5b768d8be070 ("qdl: Allow multiple Sahara images")' introduced
support for specifying multiple programmer images, by splitting the
requested programmer on ':'-character and use the pair as id and
filename specifiers. This obviously breaks absolute paths in Windows.

Reorder the programmer decoder such that if the specifier starts with an
id followed by a ':', then the specifier must be a comma-separated list
of id:filename (where ',' is not allowed in the filename).

In all other cases consider the whole specifier the one file to use as
programmer, programmer archive, or programmer specifier XML. This brings
back the ability to specify programmer by absolute path in Windows.

The parsing of the programmer specifier is also altered to not tokenize
on ':' but instead only find the first one and then treat the rest as
the filename. This makes it possible use absolute paths here as well.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2026-01-27 13:34:34 -06:00
Steve Moskovchenko
22a43e2d01 qdl: Fix config XML capitalization
Fix the capitalization of the 'ZlpAwareHost' and 'Verbose'
options, since the mis-capitalized versions seem to have no
effect on programmer behavior.

Signed-off-by: Steve Moskovchenko <stevemo@skydio.com>
[bjorn: Updated VIP digest]
Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2026-01-22 22:00:33 -06:00
Christopher Obbard
5fa6a0d124 makefile: Allow cross-building
qdl fails to cross build from source, because the Makefile hard-codes
the build architecture pkg-config. Allow pkg-config to be overwritten.

Link: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1117020
Signed-off-by: Christopher Obbard <obbardc@debian.org>
2026-01-16 08:46:42 -06:00
Steve Moskovchenko
6eeb866b15 vip: Fix integer underflow in digest count
If no table(s) of digests have been found, we'll hit an
integer underflow while trying to clean up n-1 table
entries. Fix that.

Signed-off-by: Steve Moskovchenko <stevemo@skydio.com>
Signed-off-by: Jerry Zhang <Jerry@skydio.com>
2025-12-18 18:52:57 -08:00
Bjorn Andersson
b4030fabe6 usb: Fix checkpatch warning about unnecessary parenthesis
Checkpatch was warning about unnecessary parenthesis in the change to
the ZLP handling, but Github apparently doesn't highlight warnings.

Fix this to avoid leaving the building with broken windows.

Fixes: b9ad4ceaf8 ("firehose/usb: Explicitly handle ZLP on USB read transfers")
Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-12-16 16:37:59 -08:00
Bjorn Andersson
a10cf7f5da firehose: Increase image write timeout to 60 seconds
The somewhat arbitrary timeout of 30 seconds was chosen by observing a
worst case time of ~15 seconds when flashing Hamoa and Nord. But, as
reported, flashing SAIL on Nord sometimes needs this timeout to be ~35
seconds.

While the relationship between the different factors and the needed
timeout isn't fully understood, bump the value to 60 seconds to make it
work for this case as well.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-12-16 16:31:36 -08:00
Bjorn Andersson
e033f0f205 github: Resolve liblzma-related failure in Windows builds
The Windows builds suddenly fails with:
  Cannot find path 'C:\a\_temp\msys64\mingw64\bin\liblzma-5.dll' because it does not exist.

The internet indicates that this is caused by the lack of
mingw-w64-x86_64-xz, so let's introduce this.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-12-16 16:05:57 -08:00
Loic Poulain
b9ad4ceaf8 firehose/usb: Explicitly handle ZLP on USB read transfers
The assumption that every other Firehose read transfer is null is
incorrect. A Zero-Length Packet (ZLP) is only sent when the received
transfer size is a multiple of the USB IN endpoint's Max Packet Size.
This is a way for the device to indicated end-of-transfer.

Unconditionally expecting an empty packet was not an issue with USB 2.0,
where the typical 512-byte sector size matches the bulk IN endpoint's
Max Packet Size. However, with USB SuperSpeed (Max Packet Size = 1024),
attempting to read an empty packet after a 512/1024-byte transfer is
unnecessary and fails (timeout), resulting in errors during storage
device sector size discovery:
```
waiting for programmer...
qdl: failed to read: Resource temporarily unavailable
```

Fix and move ZLP handling to bus-specific code (usb.c).

Reported-by: Danilo Leo <d.leo@arduino.cc>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
2025-12-16 14:20:22 -08:00
Bjorn Andersson
516a75ff89 Merge pull request #156 from ykaire-qti/empty-filename
Check for empty patch filenames.
2025-12-03 14:57:17 -06:00
Bjorn Andersson
a88edc58ed Merge pull request #158 from jcreedon/add_slot
firehose: Add slot flag
2025-11-27 11:49:33 -06:00
Bjorn Andersson
bd656a50a0 Merge pull request #160 from igoropaniuk/sign-compare
Address more -Wsign-compare issues
2025-11-27 11:48:49 -06:00
Igor Opaniuk
a3f6c1a569 Address more -Wsign-compare issues
Address more signedness/unsignedness issues, like:

../sahara.c: In function ‘sahara_debug64_one’:
../qdl.h:20:12: warning: comparison of integer expressions of different
   signedness: ‘long unsigned int’ and ‘int’ [-Wsign-compare]
   20 |         _x < _y ? _x : _y;      \
      |            ^
../sahara.c:286:26: note: in expansion of macro ‘MIN’
  286 |                 remain = MIN((uint64_t)(region.length - chunk), DEBUG_BLOCK_SIZE);
      |                          ^~~
../qdl.h:20:24: warning: operand of ‘?:’ changes signedness from ‘int’ to
  ‘long unsigned int’ due to unsignedness of other operand [-Wsign-compare]
   20 |         _x < _y ? _x : _y;      \

../gpt.c: In function ‘gpt_find_by_name’:
../gpt.c:255:65: warning: comparison of integer expressions of different
  signedness: ‘unsigned int’ and ‘int’ [-Wsign-compare]
  255 |                 if (*phys_partition >= 0 && gpt_part->partition != *phys_partition)

Now tools are built without any warnings when -Wsign-compare is enabled.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-11-27 15:26:26 +01:00
Yvonne Kaire
a3436e2b0a Ignore patches with empty filenames.
Some device builds include patches with empty filenames. This change adds a check to ignore those patches and prevent segmentation faults.
This approach ensures consistency with other Qualcomm tools, which also ignore such patches.

Signed-off-by: Yvonne Kaire <ykaire@qti.qualcomm.com>
2025-11-26 11:10:29 -08:00
Jacob Creedon
a3ec7fd90b firehose: Add slot flag
For SoCs that support multiple UFS devices, firehose allows the slot property. This adds a flag to set the slot parameter.

Signed-off-by: Jacob Creedon <jcreedon@gmail.com>
2025-11-26 11:03:19 -08:00
Bjorn Andersson
2db10bd0c6 Merge pull request #159 from igoropaniuk/integer_issues
[housekeeping] Integer-related compiler warnings
2025-11-26 09:56:20 -06:00
Igor Opaniuk
95e2a0d3a3 Address warnings for integer expressions of different signedness
Address warnings for comparisons of integer expressions of different
signedness, for example:

../firehose.c:384:31: warning: comparison of integer expressions of
   different signedness: ‘int’ and ‘long unsigned int’ [-Wsign-compare]
  384 |                 for (i = 0; i < ARRAY_SIZE(sector_sizes); i++) {

In all places, where signed value is casted to unsigned (size_t for
instance), there is always explicitly handling of possible negative
value beforehand

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-11-25 14:09:56 +01:00
Igor Opaniuk
db8c0c8819 vip: address always false warning
Address always false warning ([-Wtype-limits]):
../vip.c:430:15: warning: comparison of unsigned expression in ‘< 0’ is
  always false [-Wtype-limits]
  430 |         if (n < 0) {
      |               ^
../vip.c:437:15: warning: comparison of unsigned expression in ‘< 0’
  is always false [-Wtype-limits]
  437 |         if (n < 0) {

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-11-25 14:09:51 +01:00
Konrad Dybcio
661ca1cba2 Merge pull request #157 from igoropaniuk/housekeeping_readme
README/CI updates/improvements
2025-11-20 13:01:02 +01:00
Igor Opaniuk
7206903f43 README: move info about man pages to a separate section
Since manual pages can be generated on all systems
(Linux, Mac, Windows(MSYS)), move this step to a separate section.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-11-20 12:57:22 +01:00
Igor Opaniuk
b1d46164e3 github: add step for man page generation
Add a step for generating manual pages using recently added `manpages`
target in [1].

[1] commit 0a3c1175bd ("Add 'manpages' as new target to generate manpages by help2man")
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-11-20 12:39:48 +01:00
Igor Opaniuk
6b4a0adc3c README: add git to Windows build instructions
In [1] the git package is listed as a required package, add it to
the build instructions for Windows.

[1] commit 09a4f034b0 ("github: Get --version working for Windows build")
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-11-20 12:39:44 +01:00
Bjorn Andersson
80108974cf Merge pull request #154 from quic-bjorande/for-linux-msm/multi-programmer
Support for uploading multiple Sahara images to reach the Firehose stage.
2025-11-19 15:45:16 -06:00
Bjorn Andersson
57b7b56ebe README: Document the multi-image support
Document the three new mechanisms for providing multiple Sahara images
to QDL, in order to handle targets that needs to download multiple
images in order to enter Firehose mode.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-11-18 15:10:03 -06:00
Bjorn Andersson
406ede8e14 qdl: Also support Sahara config XML documents
Flattened METAs contains the relevant programmer files, as well as a
special "sahara_config" XML file, which specifies the Sahara images and
their image ids.

Support populating the Sahara images array from this XML file as well,
to make it easy to flash this type of builds.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-11-18 15:10:03 -06:00
Bjorn Andersson
4bdcaaec1a qdl: Support for "programmer archives"
Providing a long list of Sahara image ids and their respective payload
filename is cumbersome and error prone.

Introduce support for loading a "programmer archive", a CPIO archive
containing the list of Sahara images. The Sahara image id is read from
the filename of each file in the archive, and should be provided in the
form "<id>[:<filename>]".

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-11-18 15:10:03 -06:00
Bjorn Andersson
5b768d8be0 qdl: Allow multiple Sahara images
Some newer targets, such as Kaanapali, is requesting multiple images
from Sahara on the path to reach Firehose mode.

Extend the handling of the "programmer" argument to allow specifying a
comma-separated, colon-separated list of "<id>:<filename>" entries, to
specify the filenames for each of the image ids to be provided.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-11-18 14:49:58 -06:00
Bjorn Andersson
ff260604e7 sahara: Load programmer at parse time
Lugging around the filename of the to-be-loaded Sahara image(s) prevents
us from (in a later patch) load the programmer payload from an archive.

So move the loading of the specified programmer image(s) to parse time
and hold each one in memory instead.

While doing this, move the "mappings" array (i.e. the list of Sahara
images) to the stack instead, as it was already being passed as a
parameter to the relevant function(s).

sahara_run() is also extended to provide debug information about which
images are being presented to the device.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-11-18 14:49:54 -06:00
Roger Shimizu
0a3c1175bd Add 'manpages' as new target to generate manpages by help2man
* Makefile: `make manpages` generates the manpages.
* Makefile: `make clean` purges the generated manpages.
* .gitignore: Add generated manpages files.
* README.md: Add description how to make manpages for Linux.

Signed-off-by: Roger Shimizu <rosh@debian.org>
2025-11-18 14:24:02 -06:00
Igor Opaniuk
d164eb0ab5 Move __unused macro after variable definitions
Usage of __unused macro before the variable name leads
to triggering checkpatch sanity check:
CHECK: spaces preferred around that '*' (ctx:WxV)

Fix this by moving the macro to the end of a function param
declaration, like it's done in the Linux kernel code.

Fixes: a79a572f18 ("Address unused parameter warnings")
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-11-17 14:11:21 -06:00
Igor Opaniuk
a79a572f18 Address unused parameter warnings
Use __unused attribute to suppress compile warnings for callback
implementations, where some parameters aren't used. For example:

warning: unused parameter ‘qdl’ [-Wunused-parameter]
./firehose.c:257:80: warning: unused parameter ‘rawmode’ [-Wunused-parameter]
  257 | static int firehose_configure_response_parser(xmlNode *node...

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-11-13 17:31:44 -06:00
Igor Opaniuk
18f7e98632 qdl: introduce __unused macro
Introduce __unused macro to prevent warnings when a function parameter
might not be used.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-11-13 17:31:44 -06:00
Luca Weiss
09a4f034b0 github: Get --version working for Windows build
Make sure we can get the version into the binary for '--version' and
does not show up as "unknown-version" by installing git.

Additionally make sure to configure autocrlf to avoid -dirty suffix.

Signed-off-by: Luca Weiss <luca.weiss@fairphone.com>
2025-11-12 15:29:31 -06:00
Luca Weiss
752685df9b github: Fetch full history during checkout
With only a --depth=1 checkout we do not get good build info for
'qdl --version'.

Check out the full history so that the version is printed nicely.

Signed-off-by: Luca Weiss <luca.weiss@fairphone.com>
2025-11-12 15:29:31 -06:00
Roger Shimizu
2869ca68d8 Add -h/--help support to ks and ramdump tool
qdl already provides usage information by -h/--help command line
options. So port this usage information for ks and ramdump tool.

Signed-off-by: Roger Shimizu <rosh@debian.org>
2025-11-12 08:29:14 -06:00
Bjorn Andersson
8c42508fbd github: Bump macos runner versions
As announced by GitHub, the "macos-13" runners are being retired. The
x86_64 build has been using this, so bump it to "macos-15-intel", in
case we have any x86_64 users.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-11-11 13:23:48 -06:00
Bjorn Andersson
f1b6a30a1a firehose: Increase program timeout for SPINOR
The ZLP for SPINOR writes has been measured to take up to 15 seconds to
complete, resulting in timeouts even with the new 10 second value.

Provide a longer timeout (double the currently measured value)
specifically for SPINOR.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-11-11 09:09:38 -06:00
Bjorn Andersson
57910d9696 qdl: Be explicit about storage types
Rather than accepting an arbitrary string for "storage type", pass this
through a variety of strcmp() and then passing it verbatim to
<configure>, define the supported storage types, decode the passed
value, and clean up the various checks.

While doing so, extend the help text to document that we do support both
"nand" and "spinor" (the latter with some trouble).

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-11-11 09:09:38 -06:00
Bjorn Andersson
3433cd77c3 README: Add blank lines around code blocks
markdownlink requires that there are blank rounds around code blocks.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-11-11 09:09:38 -06:00
Dmitry Baryshkov
5c5010c4fd Merge pull request #147 from igoropaniuk/misc_fixes
Misc fixes
2025-11-05 17:36:53 +02:00
Igor Opaniuk
e0d254190b vip: fix path validation
When the target directory doesn’t exist (stat() fails or not a dir),
it logs an error but continues, failing later when opening files.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-11-04 13:14:31 +01:00
Igor Opaniuk
dd7fb4f002 util: address memory leak in attr_as_bool()
The xmlChar* value is never xmlFree()'d before returning.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-11-04 13:14:25 +01:00
Loic Poulain
8c0fd741ee usb: increase firehose raw write timeout to 10 seconds
During QRB2210 provisioning, a USB write failure has been
observed early in the Firehose phase (first block write).
This issue has been traced to a timeout during the USB
bulk transfer of the Zero-Length Packet (ZLP).

In some conditions, the ZLP transfer may take longer than
the current timeout, up to approximately 1.7 seconds.

The issue specifically occurs after a prior large eMMC write
operation (e.g., during a previous QDL session). It could then
be related to internal eMMC I/O operations or timing delays
affecting the USB ack.

To resolve this issue, we introduce a timeout parameter to the
qdl_write function, consistent with the existing qdl_read, and
we increase the timeout to 10 seconds for Firehose raw binary
write operations to avoid 'false-positive' timeout.

Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
2025-11-03 14:37:47 -06:00
Amit Kucheria
92b14a1c58 Add build instructions for MacPorts
Mac users use Homebrew or MacPorts to get their "unix" tools. 

Add instructions to build on MacPorts users.

Signed-off-by: Amit Kucheria <amit.kucheria@oss.qualcomm.com>
2025-10-24 08:08:32 -05:00
Michael Scott
f3b4fd4fdc gpt: gpt_load_table_from_partition: fix off-by-one error for num_sectors
When using first_lba and last_lba information from the GPT entry to
calculate the number of sectors, we need to add 1 to the result.

Imagine if both values were 1.  This means there is 1 sector of data
and the first and last LBA values are same.  However, the current
calculation is: num_sectors = last_lba - first_lba.

In this case, it would return a value of 0.  (Always 1 less sector
than it should).

Tested on the CDT partition of RB3 Gen2:
In the rawprogram3.xml file it has num_partition_sectors="32"
(4096 byte sector size).  This should result in a binary size of
131072.

Pre-patch:
$ qdl --storage ufs prog_firehose_ddr.elf read 3/cdt cdt-bad.bin
waiting for programmer...
partition 6 has not GPT header
partition 7 has not GPT header
0 patches applied
read "cdt-bad.bin" successfully
$ ls -l cdt-bad.bin
-rw-r--r-- 1 user user 126976 Sep 16 21:02 cdt-bad.bin

Post-patch:
$ qdl --storage ufs prog_firehose_ddr.elf read 3/cdt cdt-good.bin
Waiting for EDL device
waiting for programmer...
partition 6 has not GPT header
partition 7 has not GPT header
0 patches applied
read "cdt-good.bin" successfully
$ ls -l cdt-good.bin
-rw-r--r-- 1 user user 131072 Sep 16 21:04 cdt-good.bin

Signed-off-by: Michael Scott <mike.scott@oss.qualcomm.com>
2025-09-17 19:56:07 -05:00
Bjorn Andersson
1b9974c198 patch: Don't spam the log when no patch file was loaded
When invoking QDL in a way that doesn't involve patching (e.g. by
invoking a read) the "0 patches applied" is annoying and can be
confusing.

Make this statement conditional on patch files actually having been
loaded.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-17 09:25:46 -05:00
Bjorn Andersson
33e6ec77ac sahara: Print an error when device isn't reachable
Sometimes the device ends up in a funky state, resulting in the initial
USB read to fail. Rather then just silently exiting with an error code,
actually print a message to inform the user.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-17 09:25:14 -05:00
Bjorn Andersson
841a0a8e7f firehose: Improve startup time significantly
The firehose_read() at the start of firehose_run() serves the purpose of
waiting for the programmer to show up and to consume any <log/> messages
spat out, so that we're ready to invoke the <configure/>.

On MSM8916 there are no <log/> entries, so this is a 5 second loop
hitting -ETIMEDOUTs in firehose_read(). On other tested targets, it does
drain the <log/> entries and then hits -ETIMEDOUTs for the remainder of
5 seconds.

On the newer targets, we could perhaps determine when the programmer is
up by looking for <log/> entries, but this doesn't help the MSM8916
model and it doesn't fit with the current implementation.

If we instead tweak the timeouts and error handling of
firehose_configure(), we can speculatively attempt to configure the
programmer until success.

This meets both the observed models, and shows savings between 4 and 4.5
seconds in startup time across the tested devices.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-17 09:24:55 -05:00
Bjorn Andersson
5449037f1c ufs: Extract provisioning from firehose_run()
firehose_run() is clearly sequential, except if UFS provisioning is
taken place, then it's a completely different sequence and an early
return - almost like it's two separate functions crammed into one.

Split firehose_provision() from firehose_run() and make the decision in
qdl.c instead.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-17 09:24:55 -05:00
Bjorn Andersson
2580547165 firehose: Unbreak db410c/msm8916 support
Somewhere along the road, we broke support for the programmer for
msm8916 (and presumably other similar socs). Attempts to operate
firehose against these boards would exclusively result in USB timeouts
on write.

The reason for this is that, in contrast to all other commonly used
targets, the programmer for MSM8916 generates <log/> messages after the
<response/> message and writes are not services until all the <log/>
entries are consumed.

Rework the read loop, to continue reading messages until a timeout or an
error occurs, and then return the response status, if one was previously
found. This cost us 100ms per attempt to read a response, but I've not
found any other way to determine that we've consumed all responses.

Naively doing this does however break <read/>, as immediately following
the response with "rawmode=true" the device starts sending the data.
Adding support for parsing out "rawmode" and immediately terminating the
loop solves this, and reduces the impact of the 100ms penalty (as it no
longer applies to either <program/> or <read/>.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-17 09:24:55 -05:00
Bjorn Andersson
d561ed5bd2 firehose: Provide progress bar on read as well
Rather than letting the user wait in silence for read operations add a
progress bar, in the same way as it's done for writing.

Make it conditional on the "quiet" variable, to avoid spamming the log
when the GPT parser etc kicks in.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-17 09:23:48 -05:00
Bjorn Andersson
08c4657e8d checkpatch: Skip list.h
list.h has a number of checkpatch warnings (like multiple evaluate) that
I don't intend to fix at this point. So drop list.h from checkpatch to
not prevent the checker from running.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-17 09:23:08 -05:00
Bjorn Andersson
ccc1b6835b qdl: Fail fast when programmer is unavailable
Having the tool fail due to missing images before we establish the USB
connection turns out to be rather nice, so let's do the same thing with
the flash programmer.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-12 10:36:09 -05:00
Bjorn Andersson
ce175f2e27 Introduce a linked list abstraction
For some reason qdl was written with open-coded linked list operations
throughout the implementation, resulting in ugly boiler plate code
sprinkled over the code base.

Integrate the linked list abstraction used in a few other of our
open-source projects to clean up the code.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-12 10:36:09 -05:00
Bjorn Andersson
6a67b6bd64 sparse: Parse sparse files at load time
The nested loop and the parsing of the sparse files during application
isn't very clean, and results in parse errors coming late in the
process.
It also results in two different lists of program elements, and the
sparse entries are currently not cleaned up after application.

Instead update the program parser, such that when a sparse entry is
found the sparse image is parsed and the chunks are added to the program
list.
The filename member of the original program entry is dropped, to prevent
programming the region, while still tracking it. This could allow us to
later implement an "erase before flash" of the program segments.

This results in a much cleaner application of the program entries, and
any errors are detected up front.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-12 10:36:09 -05:00
Bjorn Andersson
3eb702d950 Move check for missing files to load time
Rather than letting the flash process get halfway through before
aborting for missing files, check that files are available while loading
the program xml files, before we start talking to the device.

In addition to a better user experience, this helps cleaning up the
upcoming change where sparse files are parsed at load time as well.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-12 10:36:09 -05:00
Bjorn Andersson
35b46a0e5e Resolve include dir at load time
Rather than waiting until program and read time to resolve the include
path, adjust the filename paths while we're parsing the XML tags.

This simplifies the execution step slightly, but more importantly it
allow us to also apply the missing-files check at load time in an
upcoming commit.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-12 10:36:09 -05:00
Bjorn Andersson
dad8b4802d qdl: Document the newly introduced read and write commands
Add documentation and usage for the newly introduced read and write
commands.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-09 16:18:12 -05:00
Bjorn Andersson
60db50966d qdl: Extend read/write support to accept GPT partition names
While already powerful, it's quite often one wants to read and write
some specific GPT partition, and manually resolving the sectors and
plugging these into either a XML file or the command line is tedious and
error prone.

Allow partition names in the address specifier of the "read" and "write"
command line actions, and when these are used read the GPTs across all
physical partitions to resolve the physical partition, start sector and
sector count for the operation.

This allow us to do things like:

  qdl prog_firehose.elf write abl_a abl2esp.elf write abl_b abl2esp.elf

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-09 16:18:12 -05:00
Bjorn Andersson
dff2ed7a72 qdl: Add "read" and "write" commands
There are a number of scenarios where one just want to write a binary
file directly to some specific location on the target, such as writing a
full disk image to LUN 0. Similarily (although not as frequent) one just
want to dump a few sectors of data for inspection.

So far the recommended way to do this has been to craft a program.xml
or a read.xml file and feed to QDL, but this is annoying.

Add support for writing/reading binary data straight to/from the device
by the means of just specifying the "write" or "read" commands on the
QDL commandline, e.g.:

  qdl prog_firehose.elf write 0 debian.img

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-09 16:18:12 -05:00
Bjorn Andersson
10d16aabc8 firehose: Support discovering sector size
When programatically issuing read and write operations they still need
to have a sector size that matches the selected storage device. But
rather than forcing the user to tell us what the sector size is,
implement a mechanism to probe for one that works.

The detected sector size is stored in the qdl context and is used if the
read or program operations don't specify one themself.

This could be used to improve user friendliness, e.g. by providing
better error messages when the wrong value is used, but this is left as
an open item.

Since the behavior of NAND-based programmers is unknown to me, the
mechanism is left disabled when this storage type is selected.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-09-09 16:18:12 -05:00
Julien Vanier
a601db1868 github: update libxml DLL bundled with Windows binary
Include new version of libxml DLL in Windows CI build.

Signed-off-by: Julien Vanier <julien@particle.io>
2025-09-08 14:16:39 -05:00
Bjorn Andersson
29f623bd8c sparse: Clean up endianness and chunk type signedness
The sparse image format is defined in little endian, not in network
order, so we should not invoke ntohs()/ntohl() when reading them.
Further, the various constants are defined in host order, so we
shouldn't ntohs()/ntohl() them either.

This also implies that the code only works for little endian host
systems today.

Drop all the ntohs()/ntohl() conversions for now, to clean out the
incorrect conversions. The task to convert from little to host is left
as an exercise for the future.

While rewriting sparse_chunk_header_parse() the large if statement is
swapped for a select, to improve readability with a single successful
exit at the end and scattered exit returns throughout.

Fixes: 02c008adfd ("add support sparse attribute")
Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-08-28 10:42:08 -05:00
Bjorn Andersson
a83406f21b sparse: Handle larger FILL and DONT_CARE chunks
The unsparsed chunk size is expressed in units of blk_sz. For RAW chunks
this is then limited to (just under) 32-bits due to the total_sz
(sparsed chunk size), but FILL and DONT_CARE can be much bigger.

But doing the size calculation on the 32-bit integers overflows and any
upper bits are lost, and the unsparsed result is wrong. So, switch this
to 64-bit instead.

It's unclear what happens if we pass a larger num_sectors to the
programmer, so this limitation is left, and checked for.

Fixes: 02c008adfd ("add support sparse attribute")
Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-08-28 10:42:08 -05:00
Bjorn Andersson
739b1ad4db sparse: Handle larger-than-32bit sparse images
When the sparse image parser finds a RAW chunk, it queries the current
file offset in the sparse image and store this for the programming
phase. But the offset is stored in a 32-bit unsigned int, so when the
sparse image passes 4GB the program entries start to refer to the wrong
data.

Split the fill_value and offset into dedicated fields and give them both
their specific type, to avoid any confusion related to the size of these
data types.

Fixes: 02c008adfd ("add support sparse attribute")
Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2025-08-28 10:42:08 -05:00
Sam Freund
cd32723503 Fix linux command to install libusb
Signed-off-by: Sam Freund <techguy763@gmail.com>
2025-07-04 15:02:54 +02:00
Igor Opaniuk
9e5195cbe1 sahara: fix memory leak
Fix memory leak, buffer have to be freed if file is not opened
successfully in sahara_debug64_one().

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-30 14:46:17 +02:00
Bjorn Andersson
ce6faee104 Merge pull request #118 from igoropaniuk/readme_update
Readme update + cosmetic improvements
2025-06-26 15:32:35 -05:00
Bjorn Andersson
a9deec3349 Merge branch 'bmx666-sparse'
Manually merge pull request #37 in order to fix the outstanding
checkpatch SPDX warning.
2025-06-26 15:26:36 -05:00
Maxim Paymushkin
02c008adfd add support sparse attribute
Signed-off-by: Maxim Paymushkin <maxim.paymushkin.development@gmail.com>
2025-06-26 13:19:21 +02:00
Igor Opaniuk
3250ce0026 README: add details about contribution and license
1. Add build/license badges.
2. Add details about contribution workflow and "make check-cached"
   usage.
3. Fix make cmd description for Windows.
4. Add details about running tests.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-25 15:42:39 +02:00
Igor Opaniuk
228ab33af8 make: simplify check-cached target
Simplify "check-cached" target, as the previous sometimes
provides wrong results.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-25 15:27:35 +02:00
Maxim Paymushkin
4288cebfdb program: add sparse attribute
Signed-off-by: Maxim Paymushkin <maxim.paymushkin.development@gmail.com>
2025-06-25 01:30:24 +02:00
Maxim Paymushkin
7447251b25 util: add attr_as_bool
Signed-off-by: Maxim Paymushkin <maxim.paymushkin.development@gmail.com>
2025-06-25 01:30:19 +02:00
Bjorn Andersson
80b45a99fe Merge pull request #116 from igoropaniuk/checkpatch_rework
Checkpatch exceptions and rework
2025-06-24 16:36:46 -05:00
Igor Opaniuk
25562d208d qdl: move MIN and ROUND_UP macros
Move MIN and ROUND_UP macros to avoid code duplication.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 20:15:20 +02:00
Igor Opaniuk
d48d852ca2 makefile: add check and check-cached targets
`check` target runs checkpatch.pl on all sources files.
`check-cached` target runs checkpatch.pl on staged changes.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 20:15:20 +02:00
Igor Opaniuk
ec418a144f spdx: add missing license identifiers
Add missing SPDX license identifiers to source files.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 20:15:20 +02:00
Igor Opaniuk
3e5426d5cb program: address checkpatch issues
WARNING: Comparisons should place the constant on the right side of the test
if (NULL != xmlGetProp(node, (xmlChar *)"last_sector")) {

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 20:15:20 +02:00
Igor Opaniuk
03560e9e04 vip: address checkpatch issues
CHECK: Please use a blank line after function/struct/union/enum declarations
+};

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 20:15:20 +02:00
Igor Opaniuk
adf906c646 ux: address checkpatch issues
CHECK: Macro argument reuse 'x' - possible side-effects?
+#define MIN(x, y) ((x) < (y) ? (x) : (y))

CHECK: Macro argument reuse 'y' - possible side-effects?
+#define MIN(x, y) ((x) < (y) ? (x) : (y))

CHECK: line length of 167 exceeds 120 columns
+static const char * const progress_hashes = "########################################################################################################################";

CHECK: line length of 167 exceeds 120 columns
+static const char * const progress_dashes = "------------------------------------------------------------------------------------------------------------------------";

WARNING: please, no spaces at the start of a line
+    CONSOLE_SCREEN_BUFFER_INFO csbi;$

WARNING: please, no spaces at the start of a line
+    HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);$

WARNING: please, no spaces at the start of a line
+    if (GetConsoleScreenBufferInfo(stdoutHandle, &csbi)) {$

WARNING: suspect code indent for conditional statements (4, 16)
+    if (GetConsoleScreenBufferInfo(stdoutHandle, &csbi)) {
+		columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;

WARNING: please, no spaces at the start of a line
+    }$

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 20:15:15 +02:00
Igor Opaniuk
67472bf750 qdl: address checkpatch issues
CHECK: Macro argument 'typecast' may be better as '(typecast)' to

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 20:00:13 +02:00
Igor Opaniuk
cc4dced2f8 sahara: address checkpatch issues
CHECK: Macro argument reuse 'x' - possible side-effects?
+#define MIN(x, y) ((x) < (y) ? (x) : (y))

CHECK: Macro argument reuse 'y' - possible side-effects?
+#define MIN(x, y) ((x) < (y) ? (x) : (y))

CHECK: line length of 123 exceeds 120 columns
+		ux_debug("%-2d: type 0x%" PRIx64 " address: 0x%" PRIx64 " length: 0x%" PRIx64 " region: %s filename: %s\

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 19:58:25 +02:00
Igor Opaniuk
e46302830c util: address checkpatch issues
CHECK: Macro argument reuse 'x' - possible side-effects?
+#define MIN(x, y) ((x) < (y) ? (x) : (y))

CHECK: Macro argument reuse 'y' - possible side-effects?
+#define MIN(x, y) ((x) < (y) ? (x) : (y))

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 19:58:25 +02:00
Igor Opaniuk
a79ccefb2e sim: address checkpatch issues
Address these issues reported by checkpatch.pl script:

WARNING: void function return statements are not generally useful
+	return;
+}

WARNING: void function return statements are not generally useful
+	return;
+}

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 19:58:25 +02:00
Igor Opaniuk
fbb965174d ufs: address checkpatch issues
Address these issues reported by checkpatch.pl:
CHECK: Alignment should match open parenthesis
+int ufs_provisioning_execute(struct qdl_device *qdl,
+	int (*apply_ufs_common)(struct qdl_device *qdl, struct ufs_common *ufs),

CHECK: line length of 133 exceeds 120 columns
+	result->bWriteBoosterBufferPreserveUserSpaceEn = !!attr_as_unsigned(node, "bWriteBoosterBufferPreserveUserSpaceEn", &errors);

CHECK: Please don't use multiple blank lines

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 19:58:25 +02:00
Igor Opaniuk
eab45794d2 firehose: address checkpatch issues
Address this checkpatch issue:
CHECK: line length of 136 exceeds 120 columns
+		xml_setpropf(node_to_send, "bWriteBoosterBufferPreserveUserSpaceEn", "%d", ufs->bWriteBoosterBufferPreserveUserSpaceEn);

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 19:58:25 +02:00
Igor Opaniuk
669c2005d3 checkpatch: add more exceptions
Add more rules to ignore, as some of them incorrectly report
false positive results, for example in [1].

[1] https://github.com/linux-msm/qdl/actions/runs/15830061230/job/44624781156?pr=37
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 19:58:21 +02:00
Igor Opaniuk
2204f90bee github: create a full rep clone
Increase fetch-depth property for [1] action, which indicates that all
history for all branches and tags should be fetched.

This fix address such checkpatch issues as:

WARNING: Unknown commit id '4ed250c18436', maybe rebased or not pulled?
Fixes: 4ed250c184 ("qdl: add support for vip table of digests generation")
Error: WARNING: Unknown commit id '4ed250c18436', maybe rebased or not pulled?

[1] https://github.com/webispy/checkpatch-action
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 14:26:25 +02:00
Igor Opaniuk
7bdf416118 README: extend list of packages for Windows
Install "base-devel" package as well, which provides diff/cmp/make
tools.
Suggest to use "make" instead of "mingw32-make".

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 14:26:25 +02:00
Igor Opaniuk
4c942eea55 github: enable tests for windows
* Enable running tests on Windows
* Don't install cmake package, as it's not needed
* Install base-devel package, which contains regular make, diff and cmp
  tools.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 14:26:25 +02:00
Igor Opaniuk
fb47383c92 vip: open digest file as binary
read() returns the number of bytes read, which might be less than
provided buffer_size if there are fewer than buffer_size bytes left
in the file, or if the file was opened in text mode.

In text mode, each carriage return-line feed pair \r\n is replaced with a
single line feed character \n, which leads to failures on Windows
when reading digests from DIGEST_TABLE.bin (as we read less data than
we expect).

Fixes: 4ed250c184 ("qdl: add support for vip table of digests generation")
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 14:26:25 +02:00
Igor Opaniuk
a691179284 qdl: improve usage output
Prettify usage details and introduce explicit -h param:

Usage: qdl [options] <prog.mbn> [<program> <patch> ...]
 -d, --debug			Print detailed debug info
 -v, --version			Print the current version and exit
 -n, --dry-run			Dry run execution, no device reading or flashing
 -f, --allow-missing		Allow skipping of missing files during flashing
 -s, --storage=T		Set target storage type T: <emmc|nand|ufs>
 -l, --finalize-provisioning	Provision the target storage
 -i, --include=T		Set an optional folder T to search for files
 -S, --serial=T			Select target by serial number T (e.g. <0AA94EFD>)
 -u, --out-chunk-size=T		Override chunk size for transaction with T
 -t, --create-digests=T		Generate table of digests in the T folder
 -D, --vip-table-path=T		Use digest tables in the T folder for VIP
 -h, --help			Print this usage info

Example: qdl prog_firehose_ddr.elf rawprogram*.xml patch*.xml

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 14:26:14 +02:00
Igor Opaniuk
c70255e3e8 qdl: fix provided options to getopt_long
Fix list of provided command-line options to getopt_long() for parsing.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 14:26:14 +02:00
Igor Opaniuk
7a8b6f3115 qdl: drop enum for chunk size param
Drop enum definition for chunk size parameter.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 14:26:14 +02:00
Igor Opaniuk
8818b98e85 firehose: support VIP extension
This extends the Firehose protocol implementation to support the VIP
extension. It implements a state machine that counts the number of
packets sent, then injects the VIP table as the next RAW packet when
the Firehose programmer runs out of provided digests and needs a new table
of digests to validate the next packets. For example:

Packet 0: DigestsTableToSign.bin.mbn (53 digest + 1 digest of next table)
Packet 1: <configure>
Packet 2: <program>
Packet 3: ...
...
Packet 54: ChainedTableOfDigests0.bin (255 digests + digest of next table)
Packet 55: <program>
...
Packet 309: ChainedTableOfDigests1.bin

To enable VIP extension provide a path where previously generated VIP
tables are stored using "--vip-table-path" param:

$ qdl --vip-table-path "<vip-table-path>" prog_firehose_ddr.elf \
  rawprogram*.xml patch*.xml

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 14:26:14 +02:00
Igor Opaniuk
6454ab46f2 firehose: don't send configure packet in simulation
There is a regression in the current implementation of simulation mode,
which leads to sending two configure packets:

FIREHOSE WRITE: <?xml version="1.0"?>
<data><configure MemoryName="ufs" MaxPayloadSizeToTargetInBytes="1048576"
verbose="0" ZLPAwareHost="1" SkipStorageInit="0"/></data>

FIREHOSE WRITE: <?xml version="1.0"?>
<data><configure MemoryName="ufs" MaxPayloadSizeToTargetInBytes="0"
verbose="0" ZLPAwareHost="1" SkipStorageInit="0"/></data>

In simulated mode "remote" target can't propose different size, so
we just don't re-send configure packet.
Move the check for simulation mode to prevent sending additional
configure packets with the attribute MaxPayloadSizeToTargetInBytes=0.

Also change DIGEST_TABLE.bin sha256 hash to
a05e1124edbe34dc504a327544fb66572591353dc3fa25e6e7eafbe4803e63e0,
as amount of packets supposed to be sent to the target changed.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 14:26:14 +02:00
Igor Opaniuk
62464e4ab6 firehose: try to obtain a status when usb write fails
In some cases (for instance, in VIP mode), the Firehose programmer can
report an additional error code before stalling and can provide
valuable information about the type of error that occurred.

For example, when digest verification fails, Firehose programmer provides
additional log messages before stalling:

<data>
<log value="ERROR: Hash of data doesn't match the expected hash 179" />
</data>
<data>
<log value="INFO: Expected hash (32)" /></data>
<data>
<log value="INFO: 30 E1 49 55 EB F1 35 22 66 ..." /></data>
<data>
<log value="INFO: Calculated hash (32)" /></data>
<data>
<log value="INFO: AE BA 2E DE 99 FA 44 30 E4 ... " /></data>
...
</xml>

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-24 14:26:14 +02:00
Igor Opaniuk
54da7531ea README: extend existing readme
Convert the existing README to Markdown format and expand it with build
instructions and usage examples.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-23 13:33:16 -05:00
Igor Opaniuk
4d612bc25b github: add Markdown lint action
Add Markdown linter invocation for new pull requests for README.md.
This is using existing GitHub action from [1].

[1] https://github.com/DavidAnson/markdownlint-cli2-action
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-23 13:33:16 -05:00
Igor Opaniuk
cdcdc51a3d checkpatch: address all obvious issues
Address all obvious coding style issues caught by checkpatch.pl tool.
sha2.c and sha2.h were kept as there are.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-21 21:32:25 +02:00
Igor Opaniuk
6c0fbcce08 tests: add initial test set
Add tests make target and a simple test that executes qdl in dry-run mode
for a synthetic reference FLAT build with VIP table generation.

The FLAT build contains:
- patch0.xml
- patch1.xml
- rawprogram0.xml
- rawprogram1.xml

All binaries that these XML files point to are filled with zeros, generated
during github action  execution.

Tests ensures that the table is generated correctly by comparing
calculated and expected SHA256 hashes of DigestToSign.bin file.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-20 11:51:49 +02:00
Dmitry Baryshkov
ad123a9531 Merge pull request #112 from igoropaniuk/checkpatch_check
github: add checkpatch action
2025-06-16 12:12:36 +03:00
Igor Opaniuk
81da98bc8a github: add checkpatch action
Add checkpatch.pl invocation for new pull requests. This is using
existing GitHub action from [1].

[1] https://github.com/webispy/checkpatch-action
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-16 09:41:10 +02:00
Dmitry Baryshkov
4bb672f00f Merge pull request #111 from igoropaniuk/spdx_licenses
qdl: add SPDX license identifiers
2025-06-13 17:46:56 +03:00
Igor Opaniuk
52cb41f0d7 qdl: add SPDX license identifiers
Add SPDX-License-Identifier line to each source file that
contains license text. More details about SPDX license identifiers
can be found at [1].

The scancode-toolkit [2] was used to match license text to the correct
SPDX-License-Identifier:
$ scancode --license --copyright --html scancode_result.html ./

[1] https://spdx.org/licenses/
[2] https://github.com/nexB/scancode-toolkit
Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-13 16:27:47 +02:00
Dmitry Baryshkov
cc55fda4b2 Merge pull request #108 from igoropaniuk/vip_table_generation
VIP table generation
2025-06-13 14:43:48 +03:00
Igor Opaniuk
4ed250c184 qdl: add support for vip table of digests generation
Add support for Digests Table generation for Validated Image
Programming (VIP), which is activated when Secure Boot is enabled
on the target. VIP controls which packets are allowed to be issued to
the target. Controlling the packets that can be sent to the target is
done through hashing. The target applies a hashing function to all
received data, comparing the resulting hash digest against an existing
digest table in memory. If the calculated hash digest matches the next
entry in the table, the packet (data or command) is accepted; otherwise,
the packet is rejected, and the target halts.

This change introduces logic for VIP table generation.
In the current VIP design the first signed hash table can be a
maximum of 8 KB. Considering that it must be in MBN format, and in
addition to the raw hash table, it also includes an MBN header,
a signature, and certificates, the number of hash digests it can
contain is limited to 54 hashes (a 40-byte MBN header +
a 1696-byte hash table + a 256-byte signature + 6144 bytes of certificates).
All hashes left are stored in the additional ChainedTableOfDigests<n>.bin
files.

To generate table of digests run QDL with --create-digests param,
providing a path to store VIP tables.

As a result 3 types of files are generated:
- DIGEST_TABLE.bin - contains the SHA256 table of digests for all firehose
  packets to be sent to the target. It is an intermediary table and is
  used only for the subsequent generation of "DigestsToSign.bin" and
  "ChainedTableOfDigests.bin" files. It is not used by QDL for VIP
  programming.

- DigestsToSign.bin - first 53 digests + digest of ChainedTableOfDigests.bin.
  This file has to be converted to MBN format and then signed with sectools:

  $ sectools mbn-tool generate --data DigestsToSign.bin --mbn-version 6
    --outfile DigestsToSign.bin.mbn
  $ sectools secure-image --sign DigestsToSign.bin.mbn --image-id=VIP

  Please check the security profile for your SoC to determine which version of
  the MBN format should be used.

- ChainedTableOfDigests<n>.bin - contains left digests, split on
  multiple files with 255 digests + appended hash of next table.

For example, for 400 packets supposed to be sent to the target, these files
will be generated (all digests are 32 bytes in size):

DIGEST_TABLE.bin
 _____________
| Digest 0    |
| Digest 1    |
| etc.        |
|             |
| Digest 399  |
|_____________|

DigestsTableToSign.bin  ChainedTableOfDigests0.bin  ChainedTableOfDigests1.bin
 ___________________       ___________________       ____________
| Digest 0          |     | Digest 53         |     | Digest 308 |
| Digest 1          |     | Digest 54         |     | Digest 309 |
| etc.              |     | etc.              |     | etc.       |
| Digest 52         |     | Digest 307        |     | Digest 399 |
| Next table digest |     | Next table digest |     |____________|
|___________________|     |___________________|

When QDL is executed with --debug parameter, it will also report
Firehose packet SHA-256 hashes, for example:

FIREHOSE WRITE: <?xml version="1.0"?>
<data><patch SECTOR_SIZE_IN_BYTES="4096" byte_offset="72" filename="DISK"
physical_partition_number="5" size_in_bytes="8"
start_sector="NUM_DISK_SECTORS-1" value="NUM_DISK_SECTORS-5."/></data>

FIREHOSE PACKET SHA256: a27b1459042ea36f654c5eed795730bf73ce37ce5e92e204fe06833e5e5e1749

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-06-10 20:36:52 +02:00
Dmitry Baryshkov
5db7794e9f Merge pull request #106 from Henry-ZHR/master
Include stdlib in usb.c
2025-05-26 16:30:01 +03:00
Igor Opaniuk
824d4c067e qdl: fix identation issue in QDL_DEVICE_TYPE
Fix identation issue in QDL_DEVICE_TYPE enum definition.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-05-25 09:31:11 +02:00
Igor Opaniuk
ec0e19f068 sha2: add implementation of the sha256 digest algo
Add a minimal implementation of the SHA-256 digest algorithm.
This implementation is pulled from the OpenBSD project, and [1] [2]
were used as a base.

[1] https://github.com/openbsd/src/blob/master/lib/libc/hash/sha2.c
[2] https://github.com/openbsd/src/blob/master/include/sha2.h

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-05-25 09:31:11 +02:00
Igor Opaniuk
4e944d29da firehose: add debug output for raw packets
Add detailed output for binary raw packets sent to the target,
displaying the filename and size.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-05-25 09:31:07 +02:00
Henry-ZHR
77df84fa12 Include stdlib in usb.c
As malloc is used

Signed-off-by: Henry-ZHR <henry-zhr@qq.com>
2025-05-15 14:50:29 +08:00
Igor Opaniuk
672abb1e81 qdl: add support for dry run execution
This mode assists in validating the `rawprogram_.xml` and `patch_.xml`
files, as well as the Firehose commands that are expected to be sent
to the Firehose programmer.

Dry run implementation is also expected to be extended for
the Digests Table generation required for Firehose Validated Image
Programming (VIP).

Example of usage:
$ qdl --dry-run --serial=0AA94EFD --debug prog_firehose_ddr.elf rawprogram*.xml patch*.xml
qdl version v2.1-24-g30ac3a8-dirty
This is a dry-run execution of QDL. No actual flashing has been performed
waiting for programmer...
FIREHOSE WRITE: <?xml version="1.0"?>
<data><configure MemoryName="ufs" MaxPayloadSizeToTargetInBytes="1048576"
verbose="0" ZLPAwareHost="1" SkipStorageInit="0"/></data>

FIREHOSE WRITE: <?xml version="1.0"?>
<data><configure MemoryName="ufs" MaxPayloadSizeToTargetInBytes="0"
verbose="0" ZLPAwareHost="1" SkipStorageInit="0"/></data>

accepted max payload size: 0
FIREHOSE WRITE: <?xml version="1.0"?>
<data><program SECTOR_SIZE_IN_BYTES="4096" num_partition_sectors="131072"
physical_partition_number="0" start_sector="6" filename="efi.bin"/></data>

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-05-15 02:41:08 +02:00
Igor Opaniuk
f066304676 qdl: decouple transport logic
Decouple the flashing logic from the underlying type of communication.
This is needed for introducing simulation mode, where no real flashing is
performed, but firehose packets are used for other tasks, like
VIP table generation.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-05-15 02:41:08 +02:00
Dmitry Baryshkov
fde56c8993 Merge pull request #99 from juniskane/qcm6490_pr
usb: add new USB pid 0x901d for Qualcomm QCM6490
2025-05-14 21:45:00 +03:00
Dmitry Baryshkov
1ec12f6a1f Merge pull request #101 from monkbroc/erase-all
Allow erasing all storage types
2025-05-14 21:44:02 +03:00
Dmitry Baryshkov
c856721827 Merge pull request #103 from igoropaniuk/allow_fusing
qdl: introduce allow-fusing parameter
2025-05-14 02:33:01 +03:00
Dmitry Baryshkov
0b6b9e3b78 Merge pull request #102 from igoropaniuk/sahara_warning_fix
sahara: fix ignoring return value warning
2025-05-14 02:32:46 +03:00
Igor Opaniuk
328e06962b qdl: introduce allow-fusing parameter
Introduce the --allow-fusing parameter, which must be explicitly set
if the "secdata" partition is programmed, as it will lead to irreversible
changes (fuses will be blown during the next boot).

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-05-13 14:37:05 +02:00
Igor Opaniuk
172c3c257c program: refactor program_find_bootable_partition
Split the implementation of the program_find_bootable_partition() function
into two functions. One of these should be a generic program_find_partition()
function that can be used to search for a specific program struct in the
programs linked list.

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-05-13 14:33:27 +02:00
Igor Opaniuk
8a996e5fed sahara: fix ignoring return value warning
Fix build warning in sahara protocol implementation:
sahara.c: In function ‘sahara_debug64_one’:
sahara.c:348:25: warning: ignoring return value of ‘write’
      declared with attribute ‘warn_unused_result’ [-Wunused-result]
  348 |                         write(fd, buf, n);
      |                         ^~~~~~~~~~~~~~~~~

Signed-off-by: Igor Opaniuk <igor.opaniuk@oss.qualcomm.com>
2025-05-13 14:02:51 +02:00
Dmitry Baryshkov
3ed79bef47 Merge pull request #105 from monkbroc/windows-build
Windows build
2025-05-07 17:12:09 +03:00
Julien Vanier
ccfa1fe073 Enable additional platforms in CI
Add Linux arm64, Mac Intel and Windows arm64 to the CI build. GitHub Actions runners for those platforms are available for OSS repos for free.

Signed-off-by: Julien Vanier <jvanier@gmail.com>
2025-05-06 10:31:59 -04:00
Julien Vanier
f87efad54b Implement err functions on Windows
Implement a portable version of err, errx, warn, warnx for Windows.

Signed-off-by: Julien Vanier <jvanier@gmail.com>
2025-05-02 19:00:09 -04:00
Julien Vanier
c07419cb81 Open files in binary mode on Windows
Data files must be opened in binary mode on Windows to avoid EOF being reported early when null bytes are encountered.

Signed-off-by: Julien Vanier <jvanier@gmail.com>
2025-05-02 19:00:09 -04:00
Julien Vanier
6867a75063 Remove workers not supported on free GitHub Actions
GitHub Actions doesn't provide workers for Ubuntu arm64 or Mac x64. A paid plan with hosted runners is necessary for those architectures.

Signed-off-by: Julien Vanier <jvanier@gmail.com>
2025-05-02 19:00:09 -04:00
Julien Vanier
c69d9adc76 Compile on Windows in GitHub Actions and save build artifacts
Compile for Linux x64 and arm64, Mac x64 and arm64 and Windows x64 on GitHub Actions. Upload binaries and required libraries for testing on those systems.

Signed-off-by: Julien Vanier <jvanier@gmail.com>
2025-05-02 19:00:09 -04:00
Julien Vanier
b3dccab6a6 Add Windows compatibility
Make build compatible with Windows using MSYS2 MINGW64 compiler. Add a small compatibility file for functions that don't exist in MINGW64.

Signed-off-by: Julien Vanier <jvanier@gmail.com>
2025-05-02 19:00:09 -04:00
Julien Vanier
3a7c00b931 Use ux_info for firehose success
Report successful read and program with ux_info instead of ux_err. This matches firehose_erase

Signed-off-by: Julien Vanier <jvanier@gmail.com>
2025-05-02 16:01:10 -04:00
Julien Vanier
f73656237d Don't fail the last firehose read when the number of bytes read is 0
On Windows, the last firehose read may return 0. Don't fail a firehose read in that situation.

Signed-off-by: Julien Vanier <jvanier@gmail.com>
2025-05-02 09:54:51 -04:00
Julien Vanier
33a2f151a6 Allow erasing all storage types
Erase operations are currently only supported on devices using NAND storage. With this change, erase operations also work on devices with UFS storage.

Signed-off-by: Julien Vanier <julien@particle.io>
2025-05-02 08:12:23 -04:00
Dmitry Baryshkov
136f8e2158 CI: disable Ubuntu 20.04 builds
Ubuntu 20.04 has been disabled on GitHub. Stop using it for building
images.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
2025-05-01 19:04:03 +02:00
Dmitry Baryshkov
30ac3a8abc Merge pull request #96 from lumag/version-fix
make: don't rebuild util.o (and dependencies) unnecessary
2025-03-19 16:53:14 +02:00
Dmitry Baryshkov
ab17b2e78d make: don't rebuild util.o (and dependencies) unnecessary
Using fake target name forces make to rebuild all targets that depend on
it. Properly specify version.h as a target name and use double-colon to
let make know that it's a special build target.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
2025-03-19 16:02:11 +02:00
Juha Niskanen
c9abbe7aba usb: add new USB pid 0x901d for Qualcomm QCM6490
Signed-off-by: Juha Niskanen <juniskane@gmail.com>
2025-03-16 13:11:35 +02:00
Christopher Obbard
cfdb938111 Use provided VERSION argument before calling git
In some environments where git isn't present (e.g. in Debian pbuilder),
building qdl fails due. Fix the order such that we check if VERSION is
provided first & use that, only if VERSION is not provided then call
git directly to get the version.

Signed-off-by: Christopher Obbard <christopher.obbard@linaro.org>
2025-02-07 14:18:01 -06:00
Christopher Obbard
21966c61d0 Use VERSION instead of GITREF
The variable name VERSION is more descriptive of the code than GITREF.
Use VERSION instead.

Signed-off-by: Christopher Obbard <christopher.obbard@linaro.org>
2025-02-07 14:18:01 -06:00
Christopher Obbard
2872a8ea62 Fix version.h generation
Currently the build fails when parallelised with:

  make[1]: *** No rule to make target 'version.h', needed by 'util.o'.  Stop.

Fix the name of the Makefile dependency to fix the build error.

Fixes: https://github.com/linux-msm/qdl/issues/91
Closes: https://github.com/linux-msm/qdl/pull/92
Signed-off-by: Christopher Obbard <christopher.obbard@linaro.org>
2025-02-07 14:18:01 -06:00
Nicolas Dechesne
ad320a1a66 version: set to 'unknown-version' if unable to use git describe
If for any reasons git describe does not work (git not installed, or
not running from a git workspace), set VERSION to unknown-version.

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@oss.qualcomm.com>
2025-01-21 11:11:13 -06:00
Nicolas Dechesne
8718752d81 ramdump: display version information
* add --version CLI option
* dislay version when running in --debug mode

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@oss.qualcomm.com>
2025-01-21 11:11:13 -06:00
Nicolas Dechesne
20fab24ee0 ks: display version information
* add --version CLI option
* dislay version when running in --debug mode

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@oss.qualcomm.com>
2025-01-21 11:11:13 -06:00
Nicolas Dechesne
4800d4afc2 ks: add --debug option
It was missing , it will be used to show the version when running in
debug mode.

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@oss.qualcomm.com>
2025-01-21 11:11:13 -06:00
Nicolas Dechesne
e6469d9748 qdl: display version information
* add --version CLI option
* dislay version when running in --debug mode

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@oss.qualcomm.com>
2025-01-21 11:11:13 -06:00
Nicolas Dechesne
cc584d4560 Add infrastructure to support version information
In order to implement a proper versioning scheme, let's generate a
version.h file that defines VERSION variable built from git
workspace. The version format will be

v<MAJOR>.<MINOR>-<COMMIT##>-<SHA1>

Where:
* MAJOR and MINOR are from the most recent tag
* COMMIT## is the number of commits since last tag, to ensure
  incremental numbering
* SHA1 is an abbreviated representation of the actual commit SHA.

This requires building from a git workspace, with we are effectively
calling 'git describe' during the build.

We then define print_version() utility function to be used by all
programs.

Ensure that util.c depends on version.h and that we only update
version.h timestamps when the version actually changes to avoid
unnecessary rebuilds.

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@oss.qualcomm.com>
2025-01-21 11:11:13 -06:00
Nicolas Dechesne
a8e3f661d2 firehose: change error message to info for multiple boot partition
On some platforms, we support Boot LUN redundancy, with the boot
partitions duplicated on more than 1 LUN. Only 1 LUN can be boot
active at any time, but the platform can switch on reboot in case of
corruption. As such, when flashing a full system, we end up flashing
boot partition on several LUN, and the "first" LUN with SBL or XBL
will be used as the initial/primary boot device for the
setbootablestoragedrive command.

Up until this commit we displayed an error message in this situation,
but let's turn that into an ux_info() log instead, since it's not an
error.

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@oss.qualcomm.com>
2025-01-16 15:40:47 -06:00
Nicolas Dechesne
7b5d5da48d firehose: do not display 'no boot partition found' message
It is a valid use case to flash a single partition (such as CDT) with
QDL. In such a case when we are not flashing the entire image, we
effectively are not flashing any 'boot' partition (SBL, XBL, ...), and
we don't need to send the setbootablestoragedrive command to the
programmer. Currently we display the 'no boot partition found' message
which is confusing for users, as it looks like an error message even
though it's not. Let's turn this message into a debug log message
instead.

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@oss.qualcomm.com>
2025-01-16 15:40:47 -06:00
Bjorn Andersson
8c32a74086 ux: Introduce progress indicator
Not providing any user feedback during flashing is not very user
friendly, in particular when, in some situations, it can take minutes to
flash very large files.

Introduce a progress bar for the flashing and patching steps to provide
such feedback to the user, when stdout is determined to be a terminal of
sufficient size (i.e. tools programmatically invoking qdl should see no
difference).

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2024-12-20 11:18:34 -06:00
Bjorn Andersson
a0f977935f ux: Improve log printouts
QDL uses a few different styles when printing logs for the user,
with some inconsistencies, some errors, and some room for improvement.

Revisit all the ux_xyz() invocations to clean up the output from the
tool.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2024-12-20 11:18:34 -06:00
Bjorn Andersson
0e08e852bf ux: Introduce user experience wrappers
Rather than sprinkling the user experience decisions across the
implementation with prints to stdout, stderr, conditional checks for
qdl_debug etc, consolidate these into a single set of ux wrappers.

Transition all callers of printf() and fprintf() to these new wrappers,
without changing the level of content of the printouts.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2024-12-20 11:18:34 -06:00
Bjorn Andersson
321e776cb7 firehose: Handle multiple primary bootloaders
Builds with multiple copies of the primary bootloader does not make
sense, but after successfully flashing all the partitions of such build
it makes more sense to make one of them bootable, at least more than
skipping the step and saying that none was found.

Pick the first found primary bootloader and warn the user about the
situation.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2024-12-20 10:37:08 -06:00
Bjorn Andersson
23c84163aa firehose: Avoid leaking xml properties
In a few places in firehose.c the memory returned from xmlGetProp() is
leaked. Avoid this by freeing it.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2024-12-17 14:38:49 -06:00
Bjorn Andersson
aa44721fe8 sahara: Avoid closing invalid fd
In the event that no ramdump_path is provided to sahara_run() the
ramdump_dir will remain -1. For good hygiene avoid calling close() on
this invalid file descriptor.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2024-12-17 14:38:49 -06:00
Bjorn Andersson
8adc9f3192 sahara: Correct size of hello packets
Valgrind correctly reports that the write of 0x30 bytes accesses
uninitialized stack space. Pad the Sahara HELLO request and response
with the expected "reserved" entries to reach the defined 0x30 bytes to
avoid sending random stack content to the device.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2024-12-17 14:38:49 -06:00
Bjorn Andersson
ceddd78c9d util: Avoid memory leaks in xml parsing helpers
The XML parsing helpers turns libxml strings into standard C-types and
leaks the libxml memory. Fix this by freeing the libxml strings once
they are accessed.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2024-12-17 14:38:49 -06:00
Bjorn Andersson
0214cbd3b6 firehose: Free the program scratch buffer
For each <program/>  entry int he list, a scratch buffer of "max payload
size" is allocated, and lost. Free this memory instead.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2024-12-17 14:38:49 -06:00
Bjorn Andersson
b121a92752 program/patch: Free program and patch objects on exit
Continuing the journey to make QDL not leak any memory on a successful
run, introduce  operations to free the program and patch objects on
exit.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2024-12-17 14:38:49 -06:00
Bjorn Andersson
6fc456da62 usb: Release allocated resources
Every time the USB code attempts to identify and possibly open the EDL
USB device, the libusb device descriptor-object is leaked.

While at it, also introduce qdl_close() to clean up the overall state,
in order to approach not leaking memory in a clean QDL run.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2024-12-17 14:38:49 -06:00
Nicolas Dechesne
ad13228e6a ufs: add support for UFS WriteBooster feature
WriteBooster is a new feature added in UFS3.1. There are new
paramaters in the UFS header config. Trying to provision a 3.1+ device
without these parameters fail.

Also make sure that we don't send them on older devices.

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@oss.qualcomm.com>
2024-12-12 07:56:49 -06:00
Nicolas Dechesne
fa070e81b4 qdl: fixup usage function
--serial and --out-chunk-size are parameters which were added recently
  to qdl, but the print_usage() function was not updated accordingly.

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@oss.qualcomm.com>
2024-11-26 23:31:25 -06:00
Nicolas Dechesne
f63c4df679 .github: use actions/checkout v4
v2 has been deprecated for a while now, and generates a bunch of
warnings each time an action is executed.

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@oss.qualcomm.com>
2024-11-05 18:16:52 -06:00
Bjorn Andersson
b068cc58d9 firehose: Allow bConfigDescrLock to be passed as 1
During the introduction of UFS provisioning, the author left behind an
additional safety measure of forcing the user to explicitly modify the
source and recompile QDL in order to be able to lock the provisioning,
to avoid users accidentally doing so.

While it does reduce the risk of irreversable mistakes, it's not
suitable when QDL is distributed as a binary - and the user already need
to set bConfigDescrLock both in provided XML files and on command line.

So, let's remove the safety net...

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
2024-10-10 15:17:05 +02:00
Milosz Wasilewski
f8fae69796 qdl: firehose: program: fail on missing file
When parsing XML files the specified binary to be flashed may not be
present. The default behaviour of QDL is to ignore missing file. This is
sometimes undesireble.

This patch changes the default behaviour. If the file to be flashed
can't be found qdl will exit with error. An optional flag --allow-missing
is introduced. It will allow to skip missing files during flashing procedure.
Default value of the flag is false.

Signed-off-by: Milosz Wasilewski <quic_mwasilew@quicinc.com>
2024-10-01 15:19:26 +02:00
Bjorn Andersson
cbd46184d3 usb: Allow overriding bulk write size and increase default
Since commit 760b3dffb0 ("qdl: Rework qdl_write to limit write sizes
to out_maxpktsize") outgoing transfers has been chunked into blocks of
wMaxPacketSize.

As reported by Wiktor Drewniak, Maksim Paimushkin, and Luca Weiss in [1]
there's huge benefits to be found in reverting this change, but out of
caution the limit was untouched.

With the transition to libusb, the throughput dropped another ~15% on my
machine. The numbers for HighSpeed and SuperSpeed are also in the same
ballpark.

With SuperSpeed, benchmarking of different chunk sizes in the megabyte
range shows improvement over these numbers in the range of 15-20x on the
same machine/board combination.

The bug report related to the reduction in size describes a host machine
running out of swiotlb, from the fact that we requested 1MB physically
contiguous memory. It's unclear if attempts was made to correct the
kernel's behavior. libusb does inquiry the capabilities of the kernel
and will split the buffer and submitting multiple URBs at once, as
needed.  While no definitive guidance has been found, multiple sources
seems to recommend passing 1-2MB of buffer to libusb at a time.  So,
let's move the default chunk size back up to 1MB and hope that libusb
resolves the reported problem.

Additionally, introduce a new option --out-chunk-size, which allow the
user to override the chunk size. This would allow any user to reduce the
size if needed, but also allow the value to be increased as needed.

[1] https://github.com/linux-msm/qdl/pull/39

Reported-by: Wiktor Drewniak
Reported-by: Maksim Paimushkin
Reported-by: Luca Weiss
Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-06-10 10:33:19 -05:00
Collin
a1cd535439 qdl: Add support for <read> operations
Being able to read the content of the flash is useful for e.g. making
backups or for testing purposes, add support for a new type of XML
containing <read> tags and execute these operations after
flashing and patching.

[bjorn: Rebased on master, updated commit message, moved read_op_exec after patching]
Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-06-10 10:33:05 -05:00
Bjorn Andersson
a60d45c3b7 usb: Correct libusb timeout handling
The documentation for libusb_bulk_transfer() specifies that in the event
that the read or write request is split into multiple chunks, some of
these may still be transferred despite a LIBUSB_ERROR_TIMEOUT is
returned.

In the transition to libusb this detail was missed and completed
read transfers are sometimes considred to be timeouts and the data
discarded, obviously resulting in failure to continue.

Consider the "transferred" value in the event of timeout, to avoid this.

Although not yet spotted in testing, the same logic is introduced for
the write path.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-06-07 22:19:40 +02:00
Bjorn Andersson
c647d6d9eb firehose: Always free response xmlDoc
The xmlDoc returned by firehose_response_parse() needs to be freed
regardless of what message it contains.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-06-07 22:10:57 +02:00
Bjorn Andersson
b761963272 firehose: Remove unwanted timeout warning
During boot and following the reset, a firehose_read() is performed to
drain any LOG messages from the device, but as there's no response to be
read these operations runs until the defined timeout happens - at which
time "firehose operation timed out" is printed to stderr, confusing
users to believe an error condition has occurred.

The only operation that not already print an error message is
firehose_reset(), so add this and remove the error print from
firehose_read().

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-06-07 22:10:57 +02:00
Bjorn Andersson
adfdcc6b01 firehose: Clean up return values
firehose_read() uses -errno to denote errors, -1 to represent NAK, 1 to
represent ACK and 0 to represent that we got a LOG and should read
again. This choice is unintuitive and choosing to overload errno and NAK
on the negative value space isn't awesome.

Additionally, firehose_run() does in some cases return -errno and others
-1.

Clean this up by defining ACK and NAK in the non-negative value space
and use the negative value space for errno. Use -EAGAIN to signal that
firehose_read() should make another read attempt.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-06-07 22:10:57 +02:00
Bjorn Andersson
1d8a3fff7d usb: Attempt detaching kernel driver
In the event that the kernel have some other driver attached to the
device the attempt claim of the interface will fail.

Lost in the libusb conversion was a call to USBDEVFS_DISCONNECT to first
detach any such drivers. Reintroduce this by invoking
libusb_detach_kernel_driver().

As with some other libusb functions there are multiple return values
denoting "success", so rely on libusb_claim_interface() to catch the
actual errors.

Reported-by: Maxim Akristiniy
Suggested-by: Maxim Akristiniy
Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-06-07 21:50:57 +02:00
Bjorn Andersson
8b01b6254e ci/build: Build across different Ubuntu variants
The transition to libusb was tested on a few different Linux distros,
but apparently not Ubuntu 20.04, which contains a slightly older version
of the libusb includes.

Add Ubuntu 20.04 as a CI target to catch more such issues, and for good
measure also add 24.04.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-06-05 22:34:22 +02:00
Bjorn Andersson
95be64392f usb: Allow building with older libusb
libusb commit f0cce43f882d ("core: Fix definition and use of enum
libusb_transfer_type") split the types of split transfer type and
endpoint transfer, introducing LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK.

The result is that when building with older libusb, such the one
available on Ubuntu 20.04 the build fails with the following error:

usb.c:84:16: error: ‘LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK’ undeclared (first use in this function); did you mean ‘LIBUSB_TRANSFER_TYPE_BULK’?

Introduce an alias using the preprocessor to make available the new
define when building against the older version of libusb headers.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-06-05 22:34:05 +02:00
Konrad Dybcio
aeb70e0645 firehose: Prevent potential overflow in info print
also fixes the improper format specifier i suppose

CodeQL reports:

Multiplication result may overflow 'unsigned int' before it is converted to 'long'.

Signed-off-by: Konrad Dybcio <konrad.dybcio@linaro.org>
2024-05-28 10:50:49 -05:00
Konrad Dybcio
b1ec56fc9e firehose: Fix xml_setpropf format for payload_size
CodeQL reports:

This argument should be of type 'int' but is of type 'unsigned long'.

Signed-off-by: Konrad Dybcio <konrad.dybcio@linaro.org>
2024-05-28 10:50:49 -05:00
Ricardo Salveti
fac09b2980 Makefile: prefer append on CFLAGS / LDFLAGS
Append allows an external build system to pass custom cflags/ldflags
to the local build system (make).

Useful with Yocto / OE as additional options can be given besides what
is set with a force set in the Makefile.

Signed-off-by: Ricardo Salveti <ricardo@foundries.io>
2024-05-28 10:49:55 -05:00
Konrad Dybcio
b18bca7d28 ci/codeql: Add required permissions
Signed-off-by: Konrad Dybcio <konrad.dybcio@linaro.org>
2024-05-10 18:05:06 +02:00
Bjorn Andersson
ba6b6e5b3a github: Add CodeQL workflow
Add CodeQL workflow to get some code analysis.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-05-10 17:58:28 +02:00
Luca Weiss
0132a8eb5a program: Clean up error handling in program_load
Main difference is cleaning up 'doc' in case of errors.
2024-05-10 17:58:01 +02:00
Luca Weiss
7cc2748c61 program: fail correctly on unrecognized tags
Previously the message would be printed that the tag gets ignored but in
reality the 'errors' variable will still be initialized as -EINVAL so
the if below would return an error.

  [PROGRAM] unrecognized tag "zeroout", ignoring
  qdl: program_load rawprogram0.xml failed

But since we want this qdl to error out on unrecognized tags, modify the
message and make it clear in the code that this is an error path.
2024-05-10 17:58:01 +02:00
Bjorn Andersson
25ce7fad22 usb: Allow selecting board by serial number
When working on a host with multiple boards attached being able to
select a specific board by serial number becomes necessary.

In the EDL USB descriptors a device serial number is available as part
of the iProduct string, so this can be used for comparison.

As libusb requires a handle the libusb_open() needs to be moved into the
loop.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-05-10 17:57:18 +02:00
Bjorn Andersson
37b0d0cf69 usb: Replace USBDEVFS interface with libusb
In order to support selecting board based on serial number the iProduct
field needs to be inspected, with the hand-rolled parsing of the USB
descriptors this becomes cumbersome.

Furthermore the direct use of Linux's USBDEVFS creats an unnecessary
dependency on the host OS being Linux.

It's unclear why libusb wasn't choosen in the first place, perhaps it
relates to the faint memory of 0.1 vs 1.0 packaging issues?

Move to libusb-1.0 in order to resolve these issues, as well as clean up
the code a bit.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-05-10 17:57:18 +02:00
Bjorn Andersson
983ec51d02 github: Introduce build workflow
Build the project across Ubuntu and macOS.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-05-09 23:40:58 +02:00
Bjorn Andersson
5f2f82a526 ramdump: Add support for filtering which segments to extract
The typical ramdump covers the entire DDR, which on modern devices can
be huge. But sometimes one is only interested in one or more specific
segments.

Parse the optional, comma-separated, argument to qdl-ramdump, and use
this to skip not requested segments.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-05-08 18:29:38 +02:00
Bjorn Andersson
2fd923ec97 qdl: Add ramdump support
Expose the newly introduce Sahara implementation for ramdump support to
the user, by introducing the qdl-ramdump utility.

The -o option can be used to specify the output directory, where files
will be stored.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-05-08 18:29:38 +02:00
Bjorn Andersson
5b69b112f5 qdl: Extract USB accessor functions
In preparation for the introduction of a new ramdump utility, extract
out the USB functions.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-05-08 18:29:38 +02:00
Bjorn Andersson
74779c7a77 qdl: Clean up unused include files
With this upcoming extraction of USB accessors, the includes should be
cleaned up. Start by first cleaning up the currently unused ones, to
isolate the changes.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-05-08 18:29:38 +02:00
Bjorn Andersson
a10d2bf57b Makefile: Generate compile_commands.json
Introduce a make target for generating compile_commands.json to feed
clangd and the Language Server Protocol, to gain better editor
integration.

Add the relevant files to .gitignore.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-05-08 18:29:38 +02:00
Bjorn Andersson
7cc077d066 sahara: Add ramdump support
Most devices in non-production mode will upon a fatal software fault
enter a ramdump mode, where Sahara can be used to extract the full
content of DDR (and a few other regions), for post-mortem debugging.

Introduce support for this part of the protocol.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-05-08 18:29:38 +02:00
Bjorn Andersson
7d4020fedd sahara: Add defines for commands and lengths
The Sahara implementation was written without defines for any of the
commands or their sizes, making the code harder than necessary to read.
Improve this by introducing such defines.

Signed-off-by: Bjorn Andersson <quic_bjorande@quicinc.com>
2024-05-08 18:29:38 +02:00
Quincy Fleming
a629f43428 Fix gcc-14 compile errors 2024-04-09 11:36:05 +02:00
Bjorn Andersson
3b22df2bc7 Fix qdl after kickstart introduction
The introduction of kickstart support made the assumption that the EDL
will request image #0, but this is not the case. Return to the old
behavior of ignoring the image number.

The change also made the assumption that the status in done can be
trusted, but e.g. MSM8916 returns done status of 0 upon successful
fetch, so ignore this value as well in the "single payload" case.
2023-04-11 07:27:00 -05:00
Bjorn Andersson
dbcb6fed63 Merge pull request #38 from danielg4/master
Install ks utility
2023-03-22 10:30:53 -05:00
Daniel Gimpelevich
1acf511839 Install ks utility 2023-02-21 04:55:09 +00:00
SunXinzhao
392394086d qdl: Add a fix for read xml error
When xml has no last_sector, qdl will read xml failed. Add check
to see if last_sector exists
2023-01-31 12:37:16 -06:00
Jeffrey Hugo
bdcf93b4a3 Add ks utility
ks (short for kickstart) is a utility which uses sahara to load images
from the host to a device.  This is intended for "flashless boot"
devices like the Qualcomm Cloud AI 100 which rely on obtaining the
runtime firmware from the host instead of storing it on device.

While ks uses sahara, like qdl, it is different enough that a separate
utility is justified.  ks does not need USB support, instead opting for
simple open()/read()/write() operations.  ks does not need firehose.
The set of program arguments that ks needs is vastly different than the
set that qdl supports.

This initial implementation of ks defines two arguments.  Both are
required, but the image specifier argument can be specified more than
one.

A sample invocation -

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

In this example, ks is instructed to use the /dev/mhi0_QAIC_SAHARA
device node file as the "port" which the device is using for the sahara
protocol.  /opt/qti-aic/firmware/fw1.bin is mapped to id 1, and
/opt/qti-aic/firmware/fw2.bin is mapped to id 2.  If the device requests
image id 1, ks will attempt to open and read
/opt/qti-aic/firmware/fw1.bin to send to the device.  Note that
/opt/qti-aic/firmware/fw1.bin does not need to exist on the filesystem.
If ks cannot access the file (perhaps because it does not exist), then
the device will determine the next action.  This is useful for setting
up a single command in a udev rule that can handle multiple
configurations, such as an optional DDR training process.

Signed-off-by: Jeffrey Hugo <quic_jhugo@quicinc.com>
2023-01-31 12:35:20 -06:00
Jeffrey Hugo
e25c981207 sahara: Add support for multiple images
It is possible for a device to request multiple images over the sahara
protocol.  When the device issues the done response packet, the status
field indicates if the device expects to request additional images.  If
so, the protocol goes back to the start and exchanges hello messages
again.  Also, some images may be optional.  The device may be able to
proceed if the host is unable to satisfy the request.

To support this, we need several changes.

First, we need to use the done response packet status field to decide
if we are done with sahara instead of assuming that based on having
transfered a single image.

Second, we need to reference the image id that the device is requesting
and operate on the correct file.  This is a field in the read data
packet.  Since the device could request more than one image, we need
a mapping of ids to files.  Since different devices could have different
mappings, abstract this mapping into the qdl struct.  When we get a read
request from the device, we need to open the correct file, seek to the
required offset, and provide the requested segment.  The implementation
for this chooses simplicity over performance by avoiding the need to
maintain state of what image is currently requested, and if that has
been accessed before, thus we have an open fd to use which needs to be
cleaned up later.

Since the qdl struct is larger with the mapping table, move it off the
stack and instead make it a static global.  We expect to only need one
instance of it, and this gives us the benifit of zero initializing the
struct for free.  We also move the qdl struct definition into the qdl
header so that it can be shared with a similar implementation in a
future development.

Signed-off-by: Jeffrey Hugo <quic_jhugo@quicinc.com>
2023-01-31 12:35:20 -06:00
Andrew Halaney
a8d10da050 readme: Mention libudev requirement
Without libudev installed you'll get the following build error:

    qdl.c:43:10: fatal error: libudev.h: No such file or directory
       43 | #include <libudev.h>
          |          ^~~~~~~~~~~
    compilation terminated.
    make: *** [<builtin>: qdl.o] Error 1

Mention the ubuntu package that brings it in as well similar to how
libxml2 is mentioned.

Signed-off-by: Andrew Halaney <ajhalaney@gmail.com>
2022-07-18 15:52:54 -05:00
Bjorn Andersson
0fcf944ab5 makefile: Use pkg-config instead of xml2-config
Per user request, use pkg-config instead of xml2-config when generating
cflags and ldflags.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-11-10 09:44:50 -06:00
Jonathan Marek
2021b303a8 firehose: fix >4GB lseek file offsets
The byte offset value for lseek can easily exceed 4GB, cast as off_t to
avoid overflow when converting sectors to bytes for lseek.

(and define _FILE_OFFSET_BITS=64 to fix 32-bit builds too)

Signed-off-by: Jonathan Marek <jonathan@marek.ca>
2021-05-07 10:12:46 -07:00
Jonathan Marek
f02ed6abca firehose: call firehose_reset() in ufs provisioning path
Signed-off-by: Jonathan Marek <jonathan@marek.ca>
2021-05-06 19:07:36 -07:00
Jonathan Marek
2e258d9d56 firehose: drain remaining logs in firehose_reset()
Reset will only complete after all logs have been read.

Note this will result in an expected "qdl: firehose operation timed out"
error when draining the logs.

Signed-off-by: Jonathan Marek <jonathan@marek.ca>
2021-05-06 19:07:36 -07:00
Bjorn Andersson
13681fcb35 firehose: Make "start_sector" a string again
Assumed to be a remnant of early development lead to a recent transition
of "start_sector" from being represented as a string to an integer. But
it turns out that "start_sector" might be an expression, e.g. to write
something at the end of the disk.

So transition back to carry "start_sector" as a string.

Fixes: b6e0ea31d7 ("program: Make start_sector unsigned in")
Reported-by: Julien Robin
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-04-29 10:25:55 -05:00
Dmitry Baryshkov
3c0405c03d qdl: bail out with the sensible error if prog.mbn can not be opened
Rather than failing with the cryptic message, return early if prog.mbn
can not be found.

Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
2021-04-18 21:05:17 -07:00
Jonathan Marek
ec6c8a034e util: allow hexadecimal values for attr_as_unsigned (fix ufs provisioning)
dc61f8f79e broke ufs provisioning by requiring base 10 for unsigned
attributes (provisioning xml have some values in hexadecimal). strtoul()
would return 0 for these values and provisioning would fail strangely.

Signed-off-by: Jonathan Marek <jonathan@marek.ca>
2021-04-15 20:42:17 -07:00
Bjorn Andersson
650b477ca5 firehose: Drain logs on write timeout
On db410c writes sometimes fails becasue the device had more log entries
to read after the <response> and refuses writes until these are drained.

Deal with this by attempting a read when write fails with a timeout.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-04-15 21:52:00 -05:00
Bjorn Andersson
00d93b29f5 firehose: Rework firehose_read
WIth the transition from qcserial to USBFS we no longer receive multiple
messages in a single read, so we can remove the code that decode the
buffer piecemeal.

While reworking this, also introduce a mechanism so that the parsers can
pass values back to the caller of the read.

And modify the timeout handling so that the caller always has to pass a
timeout, and bump the timeouts slightly, as they are all fatal anyways.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-04-15 21:49:16 -05:00
Bjorn Andersson
7f75f17c53 fixup! program: Make start_sector unsigned in 2021-04-15 21:15:36 -05:00
Bjorn Andersson
40df480740 usb: Drop ununsed eot argument to qdl_write
qdl_write() is always called with eot=true, so drop the parameter.

As a length of 0 means we're not entering the loop and we're now always
entering the conditonal block at the end we can remove the first chunk.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-04-15 17:42:06 -05:00
Bjorn Andersson
91e609b611 usb: Drop unwanted debug print
A stray print can sometimes be seen from the udev code, drop it.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-04-15 14:23:49 -05:00
Bjorn Andersson
da8dd7139a program: Introduce erase tag support
NAND based devices comes with a few minor tweaks to the program tag and
an additional erase tag, split the program code and add the handling of
the erase tag.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-04-15 14:22:15 -05:00
Bjorn Andersson
b6e0ea31d7 program: Make start_sector unsigned in
For unknown reasons start_sector was a staring, turn it into an unsigned
integer instead.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-04-15 14:20:41 -05:00
Bjorn Andersson
56076936a6 firehose: Revamp read timeout handling
Remove the dummy sleep and reliance on USB read timeout while waiting
for the firehose payload to launch by rolling our own timeout handling
in firehose_read. While at it extend the firehose program timeouts, to
avoid issues seen when these operations for some reason takes a little
bit longer to return.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-04-15 14:15:43 -05:00
Bjorn Andersson
bead963d33 usb: Recognize SDX55
The SDX55 shows up with bInterfaceProtocol of 17, so allow this to get
past the USB filtering routine.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-04-15 12:00:44 -05:00
John Stultz
760b3dffb0 qdl: Rework qdl_write to limit write sizes to out_maxpktsize
On a number of machines, qdl could cause crashes on the host
system it ran on, due to swiotlb exaustion.

This seems to be due to 1M buffers being used during the writes.

In order to avoid this, rework qdl_write to break up the writes
into out_maxpktsize chunks.

With this patch, I no longer see host crashes when running qdl

Cc: Bjorn Andersson <bjorn.andersson@linaro.org>
Cc: Amit Pundir <amit.pundir@linaro.org>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: dragonboard-aosp@lists.96boards.org
Tested-by: Nicolas Dechesne <nicolas.dechesne@linaro.org>
Signed-off-by: John Stultz <john.stultz@linaro.org>
[bjorn: Dropped change of max_payload_size]
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-05-03 12:18:25 -07:00
Bjorn Andersson
7623ff5e1e firehose: Wait 3 seconds for payload to boot
The downstream tool has a 3 second delay to allow the firehose payload
to boot. Adding the same makes db820c successfully enter firehose mode.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-04-30 11:27:17 -07:00
Bjorn Andersson
fd48f7ce18 firehose: Lower the initial read timeout
Upon booting the firehose payload some platforms will issue log messages
others won't. Rather than waiting for 10 seconds on the ones that
doesn't wait only for a second.

Hopefully this is long enough, but the firmware synchronization and
handling of incoming log messages should be reviewed further.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-02-27 15:25:34 -08:00
Bjorn Andersson
90d3a6fee6 firehose: Remove unused firehose_nop()
We don't send any NOP requests, so remove this function for now.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-02-27 15:17:52 -08:00
Bjorn Andersson
1c958b2bc2 usb: Silence usb helpers
It's perfectly normal to "poll" for incoming messages, so silence the
warning printout in the usb accessor functions.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-02-27 15:08:02 -08:00
Bjorn Andersson
5ec4f210a0 firehose: Continue reading log entries after response
On db410c the target sends log entries after the response and refuse to
serve any subsequent requests until these are consumed, so continue
reading log entries until we get a timeout.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-02-27 15:06:32 -08:00
Nicolas Dechesne
1ac148c4e6 sahara_run: properly propagate error
We should return in case we haven't reached the end of the Sahara
init, which is when we set done to true. If done is not set to true
before returning from sahara_run() , it means something went wrong.

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@linaro.org>
2019-02-27 15:06:12 -08:00
Bjorn Andersson
8456cb2cf5 program: Correct a/b XBL match
A full build will contain both xbl_a and xbl_b, so matching on the
prefix of "xbl" will find two entries which we consider invalid.
Explicitly search for "xbl_a" instead.

Fixes: 5ea1e20c01 ("program: Match xbl in a/b scenarios")
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-02-27 09:10:12 -08:00
47 changed files with 6289 additions and 1052 deletions

35
.checkpatch.conf Normal file
View File

@@ -0,0 +1,35 @@
--no-tree
--strict
--max-line-length=120
--ignore FILE_PATH_CHANGES
--ignore EMAIL_SUBJECT
--ignore SPLIT_STRING
# NEW_TYPEDEFS reports "do not add new typedefs"
# typedef struct __attribute__((__packed__)) sparse_header {
--ignore NEW_TYPEDEFS
# PREFER_DEFINED_ATTRIBUTE_MACRO reports this kind of messages:
# WARNING: Prefer __packed over __attribute__((__packed__))
--ignore PREFER_DEFINED_ATTRIBUTE_MACRO
# PREFER_KERNEL_TYPES reports this kind of messages (when using --strict):
# "Prefer kernel type 'u32' over 'uint32_t'"
--ignore PREFER_KERNEL_TYPES
# BRACES reports this kind of messages:
# braces {} are not necessary for any arm of this statement
--ignore BRACES
# CAMELCASE reports this kind of messages:
# Avoid CamelCase: <xmlFreeDoc>
--ignore CAMELCASE
# AVOID_EXTERNS reports this kind of messages:
# externs should be avoided in .c files
# extern const char *__progname;
--ignore AVOID_EXTERNS
# COMMIT_LOG_LONG_LINE reports line lengths > 75 in commit log
# Lets ignore this
--ignore COMMIT_LOG_LONG_LINE

176
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,176 @@
name: Buildtest
on:
pull_request:
push:
jobs:
build-linux:
strategy:
fail-fast: false
matrix:
include:
- { arch: x64, os: 24, runner: ubuntu-24.04 }
- { arch: arm64, os: 24, runner: ubuntu-24.04-arm }
- { arch: x64, os: 22, runner: ubuntu-22.04 }
- { arch: arm64, os: 22, runner: ubuntu-22.04-arm }
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev help2man
- name: Build
run: make
- name: Run tests
run: make tests
- name: Generate man pages
run: make manpages
- name: Package
run: |
mkdir dist
cp `pkg-config --variable=libdir libusb-1.0`/libusb-1.0.so.0 dist
chmod 0644 dist/*
cp qdl dist
patchelf --set-rpath '$ORIGIN' dist/qdl
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: qdl-binary-ubuntu-${{matrix.os}}-${{ matrix.arch }}
path: dist/*
build-mac:
strategy:
fail-fast: false
matrix:
include:
- { sys: macos-14, arch: arm64 }
- { sys: macos-15-intel, arch: intel }
runs-on: ${{ matrix.sys }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Dependencies
run: |
brew install libxml2 help2man
- name: Build
run: make
- name: Generate man pages
run: make manpages
- name: Run tests
run: make tests
- name: Package
run: |
set -x
mkdir dist
cp `pkg-config --variable=libdir libusb-1.0`/libusb-1.0.0.dylib dist
cp `pkg-config --variable=libdir liblzma`/liblzma.5.dylib dist
chmod 0644 dist/*
cp qdl dist
if uname -a | grep -q arm64; then
LIBUSB_DIR=/opt/homebrew/opt/libusb/lib
LIBLZMA_DIR=/usr/lib
else
LIBUSB_DIR=/usr/local/opt/libusb/lib
LIBLZMA_DIR=/usr/local/opt/xz/lib
fi
install_name_tool -add_rpath @executable_path dist/qdl
install_name_tool -change $LIBUSB_DIR/libusb-1.0.0.dylib @rpath/libusb-1.0.0.dylib dist/qdl
install_name_tool -change $LIBLZMA_DIR/liblzma.5.dylib @rpath/liblzma.5.dylib dist/qdl
otool -L dist/qdl
dist/qdl || true
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: qdl-binary-macos-${{ matrix.arch }}
path: dist/*
build-windows:
strategy:
fail-fast: false
matrix:
include:
- { sys: windows-latest, arch: x64 }
- { sys: windows-11-arm, arch: arm64 }
runs-on: ${{ matrix.sys }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup MSYS2
id: msys2
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
install: >
base-devel
git
help2man
mingw-w64-x86_64-gcc
mingw-w64-x86_64-make
mingw-w64-x86_64-pkg-config
mingw-w64-x86_64-libxml2
mingw-w64-x86_64-libusb
mingw-w64-x86_64-xz
- name: Build
run: |
git config --global core.autocrlf true
make
shell: msys2 {0}
- name: Generate man pages
run: make manpages
shell: msys2 {0}
- name: Run tests
run: make tests
shell: msys2 {0}
- name: Package
shell: pwsh
run: |
$MSYS2_LOCATION = "${{ steps.msys2.outputs.msys2-location }}"
$BIN_DIR = Join-Path $MSYS2_LOCATION "mingw64\bin"
$DistDir = "dist"
New-Item -ItemType Directory -Path $DistDir | Out-Null
Copy-Item (Join-Path $BIN_DIR "zlib1.dll") $DistDir
Copy-Item (Join-Path $BIN_DIR "libxml2-16.dll") $DistDir
Copy-Item (Join-Path $BIN_DIR "libusb-1.0.dll") $DistDir
Copy-Item (Join-Path $BIN_DIR "liblzma-5.dll") $DistDir
Copy-Item (Join-Path $BIN_DIR "libiconv-2.dll") $DistDir
Copy-Item "qdl.exe" $DistDir
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: qdl-binary-windows-${{ matrix.arch }}
path: dist/*

15
.github/workflows/checkpatch.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: Checkpatch Review
on: [pull_request]
jobs:
check-patch:
name: checkpatch review
runs-on: ubuntu-latest
steps:
- name: 'Calculate PR commits + 1'
run: echo "PR_FETCH_DEPTH=$(( ${{ github.event.pull_request.commits }} + 1 ))" >> $GITHUB_ENV
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Run checkpatch review
uses: webispy/checkpatch-action@v9

36
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: CodeQL
on:
pull_request:
push:
jobs:
codeql:
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev
- name: CodeQL init
uses: github/codeql-action/init@v3
with:
languages: c-cpp
build-mode: autobuild
- name: CodeQL build
uses: github/codeql-action/autobuild@v3
- name: CodeQL analysis
uses: github/codeql-action/analyze@v3

11
.github/workflows/markdown-lint.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
name: Markdown Lint
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: DavidAnson/markdownlint-cli2-action@v20
with:
globs: |
README.md

10
.gitignore vendored
View File

@@ -1,2 +1,12 @@
*.1
*.o
qdl
qdl-ramdump
ks
*.exe
compile_commands.json
.cache
.version.h
version.h
scripts
.checkpatch-camelcase.git.

View File

@@ -1,17 +1,92 @@
OUT := qdl
QDL := qdl
RAMDUMP := qdl-ramdump
VERSION := $(or $(VERSION), $(shell git describe --dirty --always --tags 2>/dev/null), "unknown-version")
CFLAGS := -O2 -Wall -g `xml2-config --cflags`
LDFLAGS := `xml2-config --libs` -ludev
PKG_CONFIG ?= pkg-config
CFLAGS += -O2 -Wall -g `$(PKG_CONFIG) --cflags libxml-2.0 libusb-1.0`
LDFLAGS += `$(PKG_CONFIG) --libs libxml-2.0 libusb-1.0`
ifeq ($(OS),Windows_NT)
LDFLAGS += -lws2_32
endif
prefix := /usr/local
SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c ufs.c
OBJS := $(SRCS:.c=.o)
QDL_SRCS := firehose.c io.c qdl.c sahara.c util.c patch.c program.c read.c sha2.c sim.c ufs.c usb.c ux.c oscompat.c vip.c sparse.c gpt.c
QDL_OBJS := $(QDL_SRCS:.c=.o)
$(OUT): $(OBJS)
RAMDUMP_SRCS := ramdump.c sahara.c io.c sim.c usb.c util.c ux.c oscompat.c
RAMDUMP_OBJS := $(RAMDUMP_SRCS:.c=.o)
KS_OUT := ks
KS_SRCS := ks.c sahara.c util.c ux.c oscompat.c
KS_OBJS := $(KS_SRCS:.c=.o)
CHECKPATCH_SOURCES := $(shell find . -type f \( -name "*.c" -o -name "*.h" -o -name "*.sh" \) ! -name "sha2.c" ! -name "sha2.h" ! -name "*version.h" ! -name "list.h")
CHECKPATCH_ROOT := https://raw.githubusercontent.com/torvalds/linux/v6.15/scripts
CHECKPATCH_URL := $(CHECKPATCH_ROOT)/checkpatch.pl
CHECKPATCH_SP_URL := $(CHECKPATCH_ROOT)/spelling.txt
CHECKPATCH := ./.scripts/checkpatch.pl
CHECKPATCH_SP := ./.scripts/spelling.txt
MANPAGES := ks.1 qdl-ramdump.1 qdl.1
default: $(QDL) $(RAMDUMP) $(KS_OUT)
$(QDL): $(QDL_OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
clean:
rm -f $(OUT) $(OBJS)
$(RAMDUMP): $(RAMDUMP_OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
install: $(OUT)
install -D -m 755 $< $(DESTDIR)$(prefix)/bin/$<
$(KS_OUT): $(KS_OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
compile_commands.json: $(QDL_SRCS) $(KS_SRCS)
@echo -n $^ | jq -snR "[inputs|split(\" \")[]|{directory:\"$(PWD)\", command: \"$(CC) $(CFLAGS) -c \(.)\", file:.}]" > $@
manpages: $(KS_OUT) $(RAMDUMP) $(QDL)
help2man -N -n "KS" -o ks.1 ./ks
help2man -N -n "Qualcomm Download" -o qdl.1 ./qdl
help2man -N -n "Qualcomm Download Ramdump" -o qdl-ramdump.1 ./qdl-ramdump
version.h::
@echo "#define VERSION \"$(VERSION)\"" > .version.h
@cmp -s .version.h version.h || cp .version.h version.h
util.o: version.h
clean:
rm -f $(QDL) $(QDL_OBJS)
rm -f $(RAMDUMP) $(RAMDUMP_OBJS)
rm -f $(KS_OUT) $(KS_OBJS)
rm -f $(MANPAGES)
rm -f compile_commands.json
rm -f version.h .version.h
rm -f $(CHECKPATCH)
rm -f $(CHECKPATCH_SP)
if [ -d .scripts ]; then rmdir .scripts; fi
install: $(QDL) $(RAMDUMP) $(KS_OUT)
install -d $(DESTDIR)$(prefix)/bin
install -m 755 $^ $(DESTDIR)$(prefix)/bin
tests: default
tests:
@./tests/run_tests.sh
# Target to download checkpatch.pl if not present
$(CHECKPATCH):
@echo "Downloading checkpatch.pl..."
@mkdir -p $(dir $(CHECKPATCH))
@curl -sSfL $(CHECKPATCH_URL) -o $(CHECKPATCH)
@curl -sSfL $(CHECKPATCH_SP_URL) -o $(CHECKPATCH_SP)
@chmod +x $(CHECKPATCH)
check: $(CHECKPATCH)
@echo "Running checkpatch on source files (excluding sha2.c and sha2.h)..."
@for file in $(CHECKPATCH_SOURCES); do \
perl $(CHECKPATCH) --no-tree -f $$file || exit 1; \
done
check-cached: $(CHECKPATCH)
@echo "Running checkpatch on staged changes..."
@git diff --cached -- . | perl $(CHECKPATCH) --no-tree -

16
README
View File

@@ -1,16 +0,0 @@
Qualcomm Download
=================
This tool communicates with USB devices of id 05c6:9008 to upload a flash
loader and use this to flash images.
Usage:
qdl <prog.mbn> [<program> <patch> ...]
Building
========
In order to build the project you need libxml2 headers and libraries, found in
e.g. the libxml2-dev package.
With this installed run:
make

271
README.md Normal file
View File

@@ -0,0 +1,271 @@
# Qualcomm Download
[![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
[![Build on push](https://github.com/linux-msm/qdl/actions/workflows/build.yml/badge.svg)](https://github.com/linux-msm/qdl/actions/workflows/build.yml/badge.svg)
This tool communicates with USB devices of id `05c6:9008` to upload a flash
loader and use this to flash images.
## Build
### Linux
```bash
sudo apt install libxml2 libusb-1.0-0-dev help2man
make
```
### MacOS
For Homebrew users,
```bash
brew install libxml2 pkg-config libusb help2man
make
```
For MacPorts users
```bash
sudo port install libxml2 pkgconfig libusb help2man
make
```
### Windows
First, install the [MSYS2 environment](https://www.msys2.org/). Then, run the
MSYS2 MinGW64 terminal (located at `<msys2-installation-path>\mingw64.exe`) and
install additional packages needed for QDL compilation using the `pacman` tool:
```bash
pacman -S base-devel --needed
pacman -S git
pacman -S help2man
pacman -S mingw-w64-x86_64-gcc
pacman -S mingw-w64-x86_64-make
pacman -S mingw-w64-x86_64-pkg-config
pacman -S mingw-w64-x86_64-libusb
pacman -S mingw-w64-x86_64-libxml2
```
Then use the `make` tool to build QDL:
```bash
make
```
## Use QDL
### EDL mode
The device intended for flashing must be booted into **Emergency Download (EDL)**
mode. EDL is a special boot mode available on Qualcomm-based devices that provides
low-level access for firmware flashing and recovery. It bypasses the standard boot
process, allowing operations such as flashing firmware even on unresponsive devices
or those with locked bootloaders.
Please consult your devices documentation for instructions on how to enter EDL mode.
### Flash device
Run QDL with the `--help` option to view detailed usage information.
Below is an example of how to invoke QDL to flash a FLAT build:
```bash
qdl --dry-run prog_firehose_ddr.elf rawprogram*.xml patch*.xml
```
If you have multiple boards connected the host, provide the serial number of
the board to flash through `--serial` param:
```bash
qdl --serial=0AA94EFD prog_firehose_ddr.elf rawprogram*.xml patch*.xml
```
### Reading and writing raw binaries
In addition to flashing builds using their XML-based descriptions, QDL supports
reading and writing binaries directly.
```bash
qdl prog_firehose_ddr.elf [read | write] [address specifier] <binary>...
```
Multiple read and write commands can be specified at once. The ***address
specifier*** can take the forms:
- N - single number, specifies the physical partition number N to write the
***binary** into, starting at sector 0 (currently reading a whole physical
partition is not supported).
- N/S - two numbers, specifies the physical partition number N, and the start
sector S, to write the ***binary*** into (reading with an offset is not
supported)
- N/S+L - three numbers, specified the physical partition number N, the start
sector S and the number of sectors L, that ***binary*** should be written to,
or which should be read into ***binary***.
- partition name - a string, will match against partition names across the GPT
partition tables on all physical partitions.
- N/partition_name - single number, followed by string - will match against
partition names of the GPT partition table in the specified physical
partition N.
### Validated Image Programming (VIP)
QDL now supports **Validated Image Programming (VIP)** mode , which is activated
when Secure Boot is enabled on the target. VIP controls which packets are allowed
to be issued to the target. Controlling the packets that can be sent to the target
is done through hashing. The target applies a hashing function to all received data,
comparing the resulting hash digest against an existing digest table in memory.
If the calculated hash digest matches the next entry in the table, the packet
(data or command) is accepted; otherwise, the packet is rejected,and the target halts.
To use VIP programming, a digest table must be generated prior to flashing the device.
To generate table of digests run QDL with `--create-digests` param,
providing a path to store VIP tables. For example:
```bash
mkdir vip
qdl --create-digests=./vip prog_firehose_ddr.elf rawprogram*.xml patch*.xml
```
As a result 3 types of files are generated:
- `DIGEST_TABLE.bin` - contains the SHA256 table of digests for all firehose
packets to be sent to the target. It is an intermediary table and is
used only for the subsequent generation of `DigestsToSign.bin` and
`ChainedTableOfDigests\<n\>.bin` files. It is not used by QDL for VIP
programming.
- `DigestsToSign.bin` - first 53 digests + digest of `ChainedTableOfDigests.bin`.
This file has to be converted to MBN format and then signed with sectools:
```bash
sectools mbn-tool generate --data DigestsToSign.bin --mbn-version 6 --outfile DigestsToSign.bin.mbn
sectools secure-image --sign DigestsToSign.bin.mbn --image-id=VIP
```
Please check the security profile for your SoC to determine which version of
the MBN format should be used.
- `ChainedTableOfDigests\<n\>.bin` - contains left digests, split on
multiple files with 255 digests + appended hash of next table.
To flash board using VIP mode provide a path where previously generated and signed
table of digests are stored using `--vip-table-path` param:
```bash
qdl --vip-table-path=./vip prog_firehose_ddr.elf rawprogram*.xml patch*.xml
```
### Multi-programmer targets
On some targets multiple files need to be loaded in order to reach the
Firehose programmer, these targets will request multiple images over Sahara.
Three mechanisms for providing these images are provided:
#### Command line argument
The *programmer* argument, allows specifying a comma-separated list of
colon-separated "id" and "filename" pairs. Each filename should refer to the
Sahara image of the specified Sahara image id.
```bash
qdl 13:prog_firehose_ddr.elf,42:the-answer rawprogram.xml
```
#### Sahara configuration XML file
Flattened METAs does include the various images that need to be loaded to
enter Firehose mode, as well as a sahara_config XML file, which defines the
Sahara image id for each of these images.
If the specified device programmer is determined to be a Sahara configuration
XML file, it will be parsed and the referred to files will be loaded and
serviced to the device upon request.
```bash
qdl sahara_programmer.xml rawprogram.xml
```
#### Programmer archive
Directly providing a list of ids and filenames is cumbersome and error prone,
QDL therefore accepts a "*programmer archive*". This allows the user to use the
tool in the same fashion as was done for single-programmer targets.
The *programmer archive* is a CPIO archive containing the Sahara images to be
loaded, identified by the filename **id[:filename]** (*filename* is optional,
but useful for debugging). Each included file will be used to serve requests
for the given Sahara *id*.
Such an archive can be created by putting the target's programmer images in an
empty directory, then in that directory execute the command:
```bash
ls | cpio -o -H newc > ../programmer.cpio
```
*programmer.cpio* can now be passed to QDL and the included images will be
served, in order to reach Firehose mode.
## Run tests
To run the integration test suite for QDL, use the `make tests` target:
```bash
make tests
```
## Generate man pages
Manpages can be generated using `make manpages` target:
```bash
make manpages
```
## Contributing
Please submit any patches to the qdl (`master` branch) by using the GitHub pull
request feature. Fork the repo, create a branch, do the work, rebase with upstream,
and submit the pull request.
The preferred coding style for this tool is [Linux kernel coding style](https://www.kernel.org/doc/html/v6.15/process/coding-style.html).
Before creating a commit, please ensure that your changes adhere to the coding style
by using the `make check-cached` target, for example:
```bash
$ git status
On branch improvements
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: qdl.c
modified: qdl.h
$ make check-cached
Running checkpatch on staged changes...
ERROR: trailing whitespace
#28: FILE: qdl.h:32:
+^IQDL_DEVICE_USB, $
total: 1 errors, 0 warnings, 0 checks, 27 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
NOTE: Whitespace errors detected.
You may wish to use scripts/cleanpatch or scripts/cleanfile
Your patch has style problems, please review.
```
## License
This tool is licensed under the BSD 3-Clause licensed. Check out [LICENSE](LICENSE)
for more detais.

File diff suppressed because it is too large Load Diff

283
gpt.c Normal file
View File

@@ -0,0 +1,283 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <stdlib.h>
#include <string.h>
#define _FILE_OFFSET_BITS 64
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
#include "qdl.h"
#include "gpt.h"
struct gpt_guid {
uint32_t data1;
uint16_t data2;
uint16_t data3;
uint8_t data4[8];
} __attribute__((packed));
static const struct gpt_guid gpt_zero_guid = {0};
struct gpt_header {
uint8_t signature[8];
uint32_t revision;
uint32_t header_size;
uint32_t header_crc32;
uint32_t reserved;
uint64_t current_lba;
uint64_t backup_lba;
uint64_t first_usable_lba;
uint64_t last_usable_lba;
struct gpt_guid disk_guid;
uint64_t part_entry_lba;
uint32_t num_part_entries;
uint32_t part_entry_size;
uint32_t part_array_crc32;
uint8_t reserved2[420];
} __attribute__((packed));
struct gpt_entry {
struct gpt_guid type_guid;
struct gpt_guid unique_guid;
uint64_t first_lba;
uint64_t last_lba;
uint64_t attrs;
uint16_t name_utf16le[36];
} __attribute__((packed));
struct gpt_partition {
const char *name;
unsigned int partition;
unsigned int start_sector;
unsigned int num_sectors;
struct gpt_partition *next;
};
static struct gpt_partition *gpt_partitions;
static struct gpt_partition *gpt_partitions_last;
static void utf16le_to_utf8(uint16_t *in, size_t in_len, uint8_t *out, size_t out_len)
{
uint32_t codepoint;
uint16_t high;
uint16_t low;
uint16_t w;
size_t i;
size_t j = 0;
for (i = 0; i < in_len; i++) {
w = in[i];
if (w >= 0xd800 && w <= 0xdbff) {
high = w - 0xd800;
if (i < in_len) {
w = in[++i];
if (w >= 0xdc00 && w <= 0xdfff) {
low = w - 0xdc00;
codepoint = (((uint32_t)high << 10) | low) + 0x10000;
} else {
/* Surrogate without low surrogate */
codepoint = 0xfffd;
}
} else {
/* Lone high surrogate at end of string */
codepoint = 0xfffd;
}
} else if (w >= 0xdc00 && w <= 0xdfff) {
/* Low surrogate without high */
codepoint = 0xfffd;
} else {
codepoint = w;
}
if (codepoint == 0)
break;
if (codepoint <= 0x7f) {
if (j + 1 >= out_len)
break;
out[j++] = (uint8_t)codepoint;
} else if (codepoint <= 0x7ff) {
if (j + 2 >= out_len)
break;
out[j++] = 0xc0 | ((codepoint >> 6) & 0x1f);
out[j++] = 0x80 | (codepoint & 0x3f);
} else if (codepoint <= 0xffff) {
if (j + 3 >= out_len)
break;
out[j++] = 0xe0 | ((codepoint >> 12) & 0x0f);
out[j++] = 0x80 | ((codepoint >> 6) & 0x3f);
out[j++] = 0x80 | (codepoint & 0x3f);
} else if (codepoint <= 0x10ffff) {
if (j + 4 >= out_len)
break;
out[j++] = 0xf0 | ((codepoint >> 18) & 0x07);
out[j++] = 0x80 | ((codepoint >> 12) & 0x3f);
out[j++] = 0x80 | ((codepoint >> 6) & 0x3f);
out[j++] = 0x80 | (codepoint & 0x3f);
}
}
out[j] = '\0';
}
static int gpt_load_table_from_partition(struct qdl_device *qdl, unsigned int phys_partition, bool *eof)
{
struct gpt_partition *partition;
struct gpt_entry *entry;
struct gpt_header gpt;
uint8_t buf[4096];
struct read_op op;
unsigned int offset;
uint64_t lba;
char lba_buf[21];
uint16_t name_utf16le[36];
char name[36 * 4];
int ret;
unsigned int i;
memset(&op, 0, sizeof(op));
op.sector_size = qdl->sector_size;
op.start_sector = "1";
op.num_sectors = 1;
op.partition = phys_partition;
memset(&buf, 0, sizeof(buf));
ret = firehose_read_buf(qdl, &op, &gpt, sizeof(gpt));
if (ret) {
/* Assume that we're beyond the last partition */
*eof = true;
return -1;
}
if (memcmp(gpt.signature, "EFI PART", 8)) {
ux_err("partition %d has not GPT header\n", phys_partition);
return 0;
}
if (gpt.part_entry_size > qdl->sector_size || gpt.num_part_entries > 1024) {
ux_debug("partition %d has invalid GPT header\n", phys_partition);
return -1;
}
ux_debug("Loading GPT table from physical partition %d\n", phys_partition);
for (i = 0; i < gpt.num_part_entries; i++) {
offset = (i * gpt.part_entry_size) % qdl->sector_size;
if (offset == 0) {
lba = gpt.part_entry_lba + i * gpt.part_entry_size / qdl->sector_size;
snprintf(lba_buf, sizeof(lba_buf), "%" PRIu64, lba);
op.start_sector = lba_buf;
memset(buf, 0, sizeof(buf));
ret = firehose_read_buf(qdl, &op, buf, sizeof(buf));
if (ret) {
ux_err("failed to read GPT partition entries from %d:%u\n", phys_partition, lba);
return -1;
}
}
entry = (struct gpt_entry *)(buf + offset);
if (!memcmp(&entry->type_guid, &gpt_zero_guid, sizeof(struct gpt_guid)))
continue;
memcpy(name_utf16le, entry->name_utf16le, sizeof(name_utf16le));
utf16le_to_utf8(name_utf16le, 36, (uint8_t *)name, sizeof(name));
partition = calloc(1, sizeof(*partition));
partition->name = strdup(name);
partition->partition = phys_partition;
partition->start_sector = entry->first_lba;
/* if first_lba == last_lba there is 1 sector worth of data (IE: add 1 below) */
partition->num_sectors = entry->last_lba - entry->first_lba + 1;
ux_debug(" %3d: %s start sector %u, num sectors %u\n", i, partition->name,
partition->start_sector, partition->num_sectors);
if (gpt_partitions) {
gpt_partitions_last->next = partition;
gpt_partitions_last = partition;
} else {
gpt_partitions = partition;
gpt_partitions_last = partition;
}
}
return 0;
}
static int gpt_load_tables(struct qdl_device *qdl)
{
unsigned int i;
bool eof = false;
int ret = 0;
if (gpt_partitions)
return 0;
for (i = 0; ; i++) {
ret = gpt_load_table_from_partition(qdl, i, &eof);
if (ret)
break;
}
return eof ? 0 : ret;
}
int gpt_find_by_name(struct qdl_device *qdl, const char *name, int *phys_partition,
unsigned int *start_sector, unsigned int *num_sectors)
{
struct gpt_partition *gpt_part;
bool found = false;
int ret;
if (qdl->dev_type == QDL_DEVICE_SIM)
return 0;
ret = gpt_load_tables(qdl);
if (ret < 0)
return -1;
for (gpt_part = gpt_partitions; gpt_part; gpt_part = gpt_part->next) {
if (*phys_partition >= 0 && gpt_part->partition != (unsigned int)(*phys_partition))
continue;
if (strcmp(gpt_part->name, name))
continue;
if (found) {
ux_err("duplicate candidates for partition \"%s\" found\n", name);
return -1;
}
*phys_partition = gpt_part->partition;
*start_sector = gpt_part->start_sector;
*num_sectors = gpt_part->num_sectors;
found = true;
}
if (!found) {
if (*phys_partition >= 0)
ux_err("no partition \"%s\" found on physical partition %d\n", name, *phys_partition);
else
ux_err("no partition \"%s\" found\n", name);
return -1;
}
return 0;
}

10
gpt.h Normal file
View File

@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef __GPT_H__
#define __GPT_H__
struct qdl_device;
int gpt_find_by_name(struct qdl_device *qdl, const char *name, int *partition,
unsigned int *start_sector, unsigned int *num_sectors);
#endif

69
io.c Normal file
View File

@@ -0,0 +1,69 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2025, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <stdlib.h>
#include "qdl.h"
struct qdl_device *qdl_init(enum QDL_DEVICE_TYPE type)
{
if (type == QDL_DEVICE_USB)
return usb_init();
if (type == QDL_DEVICE_SIM)
return sim_init();
return NULL;
}
void qdl_deinit(struct qdl_device *qdl)
{
if (qdl)
free(qdl);
}
void qdl_set_out_chunk_size(struct qdl_device *qdl, long size)
{
qdl->set_out_chunk_size(qdl, size);
}
int qdl_open(struct qdl_device *qdl, const char *serial)
{
return qdl->open(qdl, serial);
}
void qdl_close(struct qdl_device *qdl)
{
qdl->close(qdl);
}
/**
* qdl_read() - Read a message from the device
* @qdl: device handle
* @buf: buffer to write the data into
* @len: maximum length of data to be read
* @timeout: timeout for the read, in milliseconds
*
* Returns: number of bytes read, might be zero for a ZLP
* negative errno on failure (notably -ETIMEDOUT)
*/
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout)
{
return qdl->read(qdl, buf, len, timeout);
}
/**
* qdl_write() - Write a message from the device
* @qdl: device handle
* @buf: buffer with data to be written
* @len: length of data to be written
* @timeout: timeout for write, in milliseconds
*
* Returns: number of bytes read
* negative errno on failure (notably -ETIMEDOUT)
*/
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len, unsigned int timeout)
{
return qdl->write(qdl, buf, len, timeout);
}

142
ks.c Normal file
View File

@@ -0,0 +1,142 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "qdl.h"
#include "oscompat.h"
#ifdef _WIN32
const char *__progname = "ks";
#endif
static struct qdl_device qdl;
bool qdl_debug;
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout __unused)
{
return read(qdl->fd, buf, len);
}
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len, unsigned int timeout __unused)
{
return write(qdl->fd, buf, len);
}
static void print_usage(FILE *out)
{
extern const char *__progname;
fprintf(out,
"%s -p <sahara dev_node> -s <id:file path> ...\n",
__progname);
fprintf(out,
" -h --help Print this usage info\n"
" -p --port Sahara device node to use\n"
" -s <id:file path> --sahara <id:file path> Sahara protocol file mapping\n"
"\n"
"One -p instance is required. One or more -s instances are required.\n"
"\n"
"Example:\n"
"ks -p /dev/mhi0_QAIC_SAHARA -s 1:/opt/qti-aic/firmware/fw1.bin -s 2:/opt/qti-aic/firmware/fw2.bin\n");
}
int main(int argc, char **argv)
{
struct sahara_image mappings[MAPPING_SZ] = {};
const char *filename;
bool found_mapping = false;
char *dev_node = NULL;
long file_id;
char *colon;
int opt;
int ret;
static struct option options[] = {
{"debug", no_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'v'},
{"port", required_argument, 0, 'p'},
{"sahara", required_argument, 0, 's'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "dvp:s:h", options, NULL)) != -1) {
switch (opt) {
case 'd':
qdl_debug = true;
break;
case 'v':
print_version();
return 0;
case 'p':
dev_node = optarg;
printf("Using port - %s\n", dev_node);
break;
case 's':
found_mapping = true;
file_id = strtol(optarg, NULL, 10);
if (file_id < 0) {
print_usage(stderr);
return 1;
}
if (file_id >= MAPPING_SZ) {
fprintf(stderr,
"ID:%ld exceeds the max value of %d\n",
file_id,
MAPPING_SZ - 1);
return 1;
}
colon = strchr(optarg, ':');
if (!colon) {
print_usage(stderr);
return 1;
}
filename = &optarg[colon - optarg + 1];
ret = load_sahara_image(filename, &mappings[file_id]);
if (ret < 0)
exit(1);
printf("Created mapping ID:%ld File:%s\n", file_id, filename);
break;
case 'h':
print_usage(stdout);
return 0;
default:
print_usage(stderr);
return 1;
}
}
// -p and -s is required
if (!dev_node || !found_mapping) {
print_usage(stderr);
return 1;
}
if (qdl_debug)
print_version();
qdl.fd = open(dev_node, O_RDWR);
if (qdl.fd < 0) {
fprintf(stderr, "Unable to open %s\n", dev_node);
return 1;
}
ret = sahara_run(&qdl, mappings, NULL, NULL);
if (ret < 0)
return 1;
return 0;
}

73
list.h Normal file
View File

@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: BSD-3-Clause */
/*
* Copyright (c) 2016, Linaro Ltd.
*/
#ifndef __LIST_H__
#define __LIST_H__
#include <stdbool.h>
#include <stddef.h>
struct list_head {
struct list_head *prev;
struct list_head *next;
};
#define LIST_INIT(list) { &(list), &(list) }
static inline void list_init(struct list_head *list)
{
list->prev = list;
list->next = list;
}
static inline bool list_empty(struct list_head *list)
{
return list->next == list;
}
static inline void list_add(struct list_head *list, struct list_head *item)
{
struct list_head *prev = list->prev;
item->next = list;
item->prev = prev;
prev->next = item;
list->prev = item;
}
static inline void list_del(struct list_head *item)
{
item->prev->next = item->next;
item->next->prev = item->prev;
}
#define list_for_each(item, list) \
for (item = (list)->next; item != list; item = item->next)
#define list_for_each_safe(item, tmp, list) \
for (item = (list)->next, tmp = item->next; item != list; item = tmp, tmp = item->next)
#define list_entry(item, type, member) \
container_of(item, type, member)
#define list_entry_first(list, type, member) \
container_of((list)->next, type, member)
#define list_entry_next(item, member) \
container_of((item)->member.next, typeof(*(item)), member)
#define list_for_each_entry(item, list, member) \
for (item = list_entry_first(list, typeof(*(item)), member); \
&item->member != list; \
item = list_entry_next(item, member))
#define list_for_each_entry_safe(item, next, list, member) \
for (item = list_entry_first(list, typeof(*(item)), member), \
next = list_entry_next(item, member); \
&item->member != list; \
item = next, \
next = list_entry_next(item, member)) \
#endif

76
oscompat.c Normal file
View File

@@ -0,0 +1,76 @@
// SPDX-License-Identifier: BSD-3-Clause
#ifdef _WIN32
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "oscompat.h"
extern const char *__progname;
void timeradd(const struct timeval *a, const struct timeval *b, struct timeval *result)
{
result->tv_sec = a->tv_sec + b->tv_sec;
result->tv_usec = a->tv_usec + b->tv_usec;
if (result->tv_usec >= 1000000) {
result->tv_sec += 1;
result->tv_usec -= 1000000;
}
}
void err(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "%s: ", __progname);
if (fmt) {
vfprintf(stderr, fmt, ap);
fprintf(stderr, ": ");
}
fprintf(stderr, "%s\n", strerror(errno));
va_end(ap);
exit(eval);
}
void errx(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "%s: ", __progname);
if (fmt)
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
exit(eval);
}
void warn(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "%s: ", __progname);
if (fmt) {
vfprintf(stderr, fmt, ap);
fprintf(stderr, ": ");
}
fprintf(stderr, "%s\n", strerror(errno));
va_end(ap);
}
void warnx(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "%s: ", __progname);
if (fmt)
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
}
#endif // _WIN32

25
oscompat.h Normal file
View File

@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef __OSCOMPAT_H__
#define __OSCOMPAT_H__
#ifndef _WIN32
#include <err.h>
#define O_BINARY 0
#else // _WIN32
#include <sys/time.h>
#include <stdbool.h>
void timeradd(const struct timeval *a, const struct timeval *b, struct timeval *result);
void err(int eval, const char *fmt, ...);
void errx(int eval, const char *fmt, ...);
void warn(const char *fmt, ...);
void warnx(const char *fmt, ...);
#endif
#endif

93
patch.c
View File

@@ -1,43 +1,20 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2016-2017, Linaro Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <unistd.h>
#include "patch.h"
#include "qdl.h"
static struct patch *patches;
static struct patch *patches_last;
static struct list_head patches = LIST_INIT(patches);
static bool patches_loaded;
int patch_load(const char *patch_file)
{
@@ -49,7 +26,7 @@ int patch_load(const char *patch_file)
doc = xmlReadFile(patch_file, NULL, 0);
if (!doc) {
fprintf(stderr, "[PATCH] failed to parse %s\n", patch_file);
ux_err("failed to parse patch-type file \"%s\"\n", patch_file);
return -EINVAL;
}
@@ -58,8 +35,8 @@ int patch_load(const char *patch_file)
if (node->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp(node->name, (xmlChar*)"patch")) {
fprintf(stderr, "[PATCH] unrecognized tag \"%s\", ignoring\n", node->name);
if (xmlStrcmp(node->name, (xmlChar *)"patch")) {
ux_err("unrecognized tag \"%s\" in patch-type file, ignoring\n", node->name);
continue;
}
@@ -77,38 +54,70 @@ int patch_load(const char *patch_file)
patch->what = attr_as_string(node, "what", &errors);
if (errors) {
fprintf(stderr, "[PATCH] errors while parsing patch\n");
ux_err("errors while parsing patch-type file \"%s\"\n", patch_file);
free(patch);
continue;
}
if (patches) {
patches_last->next = patch;
patches_last = patch;
} else {
patches = patch;
patches_last = patch;
}
list_add(&patches, &patch->node);
}
xmlFreeDoc(doc);
patches_loaded = true;
return 0;
}
int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct patch *patch))
{
struct patch *patch;
unsigned int count = 0;
unsigned int idx = 0;
int ret;
for (patch = patches; patch; patch = patch->next) {
if (!patches_loaded)
return 0;
list_for_each_entry(patch, &patches, node) {
if (!patch->filename)
continue;
if (!strcmp(patch->filename, "DISK"))
count++;
}
list_for_each_entry(patch, &patches, node) {
if (!patch->filename)
continue;
if (strcmp(patch->filename, "DISK"))
continue;
ret = apply(qdl, patch);
if (ret)
return ret;
ux_progress("Applying patches", idx++, count);
}
ux_info("%d patches applied\n", idx);
return 0;
}
void free_patches(void)
{
struct patch *patch;
struct patch *next;
list_for_each_entry_safe(patch, next, &patches, node) {
free((void *)patch->filename);
free((void *)patch->start_sector);
free((void *)patch->value);
free((void *)patch->what);
free(patch);
}
list_init(&patches);
}

14
patch.h
View File

@@ -1,22 +1,26 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef __PATCH_H__
#define __PATCH_H__
#include "list.h"
struct qdl_device;
struct patch {
unsigned sector_size;
unsigned byte_offset;
unsigned int sector_size;
unsigned int byte_offset;
const char *filename;
unsigned partition;
unsigned size_in_bytes;
unsigned int partition;
unsigned int size_in_bytes;
const char *start_sector;
const char *value;
const char *what;
struct patch *next;
struct list_head node;
};
int patch_load(const char *patch_file);
int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct patch *patch));
void free_patches(void);
#endif

475
program.c

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More