78 Commits
v2.2 ... v2.5

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
28 changed files with 2082 additions and 444 deletions

View File

@@ -18,11 +18,13 @@ jobs:
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
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev help2man
- name: Build
run: make
@@ -30,6 +32,9 @@ jobs:
- name: Run tests
run: make tests
- name: Generate man pages
run: make manpages
- name: Package
run: |
mkdir dist
@@ -51,20 +56,25 @@ jobs:
matrix:
include:
- { sys: macos-14, arch: arm64 }
- { sys: macos-13, arch: intel }
- { 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
brew install libxml2 help2man
- name: Build
run: make
- name: Generate man pages
run: make manpages
- name: Run tests
run: make tests
@@ -109,6 +119,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup MSYS2
id: msys2
@@ -117,14 +129,23 @@ jobs:
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: make
run: |
git config --global core.autocrlf true
make
shell: msys2 {0}
- name: Generate man pages
run: make manpages
shell: msys2 {0}
- name: Run tests

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
*.1
*.o
qdl
qdl-ramdump

View File

@@ -2,14 +2,15 @@ QDL := qdl
RAMDUMP := qdl-ramdump
VERSION := $(or $(VERSION), $(shell git describe --dirty --always --tags 2>/dev/null), "unknown-version")
CFLAGS += -O2 -Wall -g `pkg-config --cflags libxml-2.0 libusb-1.0`
LDFLAGS += `pkg-config --libs libxml-2.0 libusb-1.0`
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
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
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)
RAMDUMP_SRCS := ramdump.c sahara.c io.c sim.c usb.c util.c ux.c oscompat.c
@@ -19,13 +20,15 @@ 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")
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)
@@ -40,6 +43,11 @@ $(KS_OUT): $(KS_OBJS)
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
@@ -50,6 +58,7 @@ 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)

105
README.md
View File

@@ -11,14 +11,23 @@ loader and use this to flash images.
### Linux
```bash
sudo apt install libxml2 libusb-1.0-0-dev
sudo apt install libxml2 libusb-1.0-0-dev help2man
make
```
### MacOS
For Homebrew users,
```bash
brew install libxml2 pkg-config libusb
brew install libxml2 pkg-config libusb help2man
make
```
For MacPorts users
```bash
sudo port install libxml2 pkgconfig libusb help2man
make
```
@@ -30,6 +39,8 @@ 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
@@ -72,6 +83,37 @@ the board to flash through `--serial` param:
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
@@ -120,6 +162,57 @@ table of digests are stored using `--vip-table-path` param:
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:
@@ -128,6 +221,14 @@ To run the integration test suite for QDL, use the `make tests` target:
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

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

24
io.c
View File

@@ -38,12 +38,32 @@ 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);
}
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len)
/**
* 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);
return qdl->write(qdl, buf, len, timeout);
}

37
ks.c
View File

@@ -24,24 +24,25 @@ static struct qdl_device qdl;
bool qdl_debug;
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout)
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)
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(void)
static void print_usage(FILE *out)
{
extern const char *__progname;
fprintf(stderr,
fprintf(out,
"%s -p <sahara dev_node> -s <id:file path> ...\n",
__progname);
fprintf(stderr,
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"
@@ -53,6 +54,8 @@ static void print_usage(void)
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;
@@ -62,13 +65,14 @@ int main(int argc, char **argv)
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:", options, NULL)) != -1) {
while ((opt = getopt_long(argc, argv, "dvp:s:h", options, NULL)) != -1) {
switch (opt) {
case 'd':
qdl_debug = true;
@@ -84,7 +88,7 @@ int main(int argc, char **argv)
found_mapping = true;
file_id = strtol(optarg, NULL, 10);
if (file_id < 0) {
print_usage();
print_usage(stderr);
return 1;
}
if (file_id >= MAPPING_SZ) {
@@ -96,21 +100,28 @@ int main(int argc, char **argv)
}
colon = strchr(optarg, ':');
if (!colon) {
print_usage();
print_usage(stderr);
return 1;
}
qdl.mappings[file_id] = &optarg[colon - optarg + 1];
printf("Created mapping ID:%ld File:%s\n", file_id, qdl.mappings[file_id]);
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();
print_usage(stderr);
return 1;
}
}
// -p and -s is required
if (!dev_node || !found_mapping) {
print_usage();
print_usage(stderr);
return 1;
}
@@ -123,7 +134,7 @@ int main(int argc, char **argv)
return 1;
}
ret = sahara_run(&qdl, qdl.mappings, false, NULL, NULL);
ret = sahara_run(&qdl, mappings, NULL, NULL);
if (ret < 0)
return 1;

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

34
patch.c
View File

@@ -13,8 +13,8 @@
#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)
{
@@ -59,17 +59,13 @@ int patch_load(const char *patch_file)
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;
}
@@ -80,12 +76,21 @@ int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, s
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++;
}
for (patch = patches; patch; patch = patch->next) {
list_for_each_entry(patch, &patches, node) {
if (!patch->filename)
continue;
if (strcmp(patch->filename, "DISK"))
continue;
@@ -103,11 +108,10 @@ int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, s
void free_patches(void)
{
struct patch *patch = patches;
struct patch *patch;
struct patch *next;
for (patch = patches; patch; patch = next) {
next = patch->next;
list_for_each_entry_safe(patch, next, &patches, node) {
free((void *)patch->filename);
free((void *)patch->start_sector);
free((void *)patch->value);
@@ -115,5 +119,5 @@ void free_patches(void)
free(patch);
}
patches = NULL;
list_init(&patches);
}

View File

@@ -2,6 +2,8 @@
#ifndef __PATCH_H__
#define __PATCH_H__
#include "list.h"
struct qdl_device;
struct patch {
@@ -14,7 +16,7 @@ struct patch {
const char *value;
const char *what;
struct patch *next;
struct list_head node;
};
int patch_load(const char *patch_file);

259
program.c
View File

@@ -18,9 +18,9 @@
#include "qdl.h"
#include "oscompat.h"
#include "sparse.h"
#include "gpt.h"
static struct program *programes;
static struct program *programes_last;
static struct list_head programs = LIST_INIT(programs);
static int load_erase_tag(xmlNode *node, bool is_nand)
{
@@ -46,22 +46,14 @@ static int load_erase_tag(xmlNode *node, bool is_nand)
return -EINVAL;
}
if (programes) {
programes_last->next = program;
programes_last = program;
} else {
programes = program;
programes_last = program;
}
list_add(&programs, &program->node);
return 0;
}
static struct program *program_load_sparse(struct program *program, int fd)
static int program_load_sparse(struct program *program, int fd)
{
struct program *program_sparse = NULL;
struct program *programes_sparse = NULL;
struct program *programes_sparse_last = NULL;
char tmp[PATH_MAX];
sparse_header_t sparse_header;
@@ -77,20 +69,19 @@ static struct program *program_load_sparse(struct program *program, int fd)
* for a partition node but lacks a sparse header,
* it will be validated against the defined partition size.
* If the sizes match, it is likely that the 'sparse="true"' attribute
* was set by mistake.
* was set by mistake, fix the sparse flag and add the
* program entry to the list.
*/
if ((off_t)program->sector_size * program->num_sectors ==
lseek(fd, 0, SEEK_END)) {
program_sparse = calloc(1, sizeof(struct program));
memcpy(program_sparse, program, sizeof(struct program));
program_sparse->sparse = false;
program_sparse->next = NULL;
return program_sparse;
program->sparse = false;
list_add(&programs, &program->node);
return 0;
}
ux_err("[PROGRAM] Unable to parse sparse header at %s...failed\n",
program->filename);
return NULL;
return -1;
}
for (uint32_t i = 0; i < sparse_header.total_chunks; ++i) {
@@ -101,7 +92,7 @@ static struct program *program_load_sparse(struct program *program, int fd)
if (chunk_type < 0) {
ux_err("[PROGRAM] Unable to parse sparse chunk %i at %s...failed\n",
i, program->filename);
return NULL;
return -1;
}
if (chunk_size == 0)
@@ -110,7 +101,7 @@ static struct program *program_load_sparse(struct program *program, int fd)
if (chunk_size % program->sector_size != 0) {
ux_err("[SPARSE] File chunk #%u size %" PRIu64 " is not a sector-multiple\n",
i, chunk_size);
return NULL;
return -1;
}
if (chunk_size / program->sector_size >= UINT_MAX) {
@@ -120,51 +111,32 @@ static struct program *program_load_sparse(struct program *program, int fd)
*/
ux_err("[SPARSE] File chunk #%u size %" PRIu64 " is too large\n",
i, chunk_size);
return NULL;
return -1;
}
switch (chunk_type) {
case CHUNK_TYPE_RAW:
if (chunk_type == CHUNK_TYPE_RAW || chunk_type == CHUNK_TYPE_FILL) {
program_sparse = calloc(1, sizeof(struct program));
memcpy(program_sparse, program, sizeof(struct program));
program_sparse->pages_per_block = program->pages_per_block;
program_sparse->sector_size = program->sector_size;
program_sparse->file_offset = program->file_offset;
program_sparse->filename = strdup(program->filename);
program_sparse->label = strdup(program->label);
program_sparse->partition = program->partition;
program_sparse->sparse = program->sparse;
program_sparse->start_sector = strdup(program->start_sector);
program_sparse->last_sector = program->last_sector;
program_sparse->is_nand = program->is_nand;
program_sparse->is_erase = false;
program_sparse->next = NULL;
program_sparse->sparse_chunk_type = chunk_type;
program_sparse->num_sectors = chunk_size / program->sector_size;
program_sparse->sparse_chunk_type = CHUNK_TYPE_RAW;
program_sparse->sparse_offset = sparse_offset;
if (chunk_type == CHUNK_TYPE_RAW)
program_sparse->sparse_offset = sparse_offset;
else
program_sparse->sparse_fill_value = sparse_fill_value;
if (programes_sparse) {
programes_sparse_last->next = program_sparse;
programes_sparse_last = program_sparse;
} else {
programes_sparse = program_sparse;
programes_sparse_last = program_sparse;
}
break;
case CHUNK_TYPE_FILL:
program_sparse = calloc(1, sizeof(struct program));
memcpy(program_sparse, program, sizeof(struct program));
program_sparse->next = NULL;
program_sparse->num_sectors = chunk_size / program->sector_size;
program_sparse->sparse_chunk_type = CHUNK_TYPE_FILL;
program_sparse->sparse_fill_value = sparse_fill_value;
if (programes_sparse) {
programes_sparse_last->next = program_sparse;
programes_sparse_last = program_sparse;
} else {
programes_sparse = program_sparse;
programes_sparse_last = program_sparse;
}
break;
default:
break;
list_add(&programs, &program_sparse->node);
}
start_sector = (unsigned int)strtoul(program->start_sector, NULL, 0);
@@ -173,13 +145,16 @@ static struct program *program_load_sparse(struct program *program, int fd)
program->start_sector = strdup(tmp);
}
return programes_sparse;
return 0;
}
static int load_program_tag(xmlNode *node, bool is_nand)
static int load_program_tag(xmlNode *node, bool is_nand, bool allow_missing, const char *incdir)
{
struct program *program;
char tmp[PATH_MAX];
int errors = 0;
int ret;
int fd = -1;
program = calloc(1, sizeof(struct program));
@@ -208,18 +183,51 @@ static int load_program_tag(xmlNode *node, bool is_nand)
return -EINVAL;
}
if (programes) {
programes_last->next = program;
programes_last = program;
} else {
programes = program;
programes_last = program;
if (program->filename) {
if (incdir) {
snprintf(tmp, PATH_MAX, "%s/%s", incdir, program->filename);
if (access(tmp, F_OK) != -1) {
free((void *)program->filename);
program->filename = strdup(tmp);
}
}
fd = open(program->filename, O_RDONLY | O_BINARY);
if (fd < 0) {
ux_info("unable to open %s", program->filename);
if (!allow_missing) {
ux_info("...failing\n");
return -1;
}
ux_info("...ignoring\n");
free((void *)program->filename);
program->filename = NULL;
}
}
if (fd >= 0 && program->sparse) {
ret = program_load_sparse(program, fd);
if (ret < 0)
return -1;
/*
* Chunks were added to the program list, drop the filename of
* the parent, to prevent this from being written to the device
*/
free((void *)program->filename);
program->filename = NULL;
}
list_add(&programs, &program->node);
if (fd >= 0)
close(fd);
return 0;
}
int program_load(const char *program_file, bool is_nand)
int program_load(const char *program_file, bool is_nand, bool allow_missing, const char *incdir)
{
xmlNode *node;
xmlNode *root;
@@ -240,7 +248,7 @@ int program_load(const char *program_file, bool is_nand)
if (!xmlStrcmp(node->name, (xmlChar *)"erase"))
errors = load_erase_tag(node, is_nand);
else if (!xmlStrcmp(node->name, (xmlChar *)"program"))
errors = load_program_tag(node, is_nand);
errors = load_program_tag(node, is_nand, allow_missing, incdir);
else {
ux_err("unrecognized tag \"%s\" in program-type file \"%s\"\n", node->name, program_file);
errors = -EINVAL;
@@ -256,55 +264,24 @@ out:
return errors;
}
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
const char *incdir, bool allow_missing)
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd))
{
struct program *program;
struct program *program_sparse;
const char *filename;
char tmp[PATH_MAX];
int ret;
int fd;
for (program = programes; program; program = program->next) {
list_for_each_entry(program, &programs, node) {
if (program->is_erase || !program->filename)
continue;
filename = program->filename;
if (incdir) {
snprintf(tmp, PATH_MAX, "%s/%s", incdir, filename);
if (access(tmp, F_OK) != -1)
filename = tmp;
}
fd = open(filename, O_RDONLY | O_BINARY);
fd = open(program->filename, O_RDONLY | O_BINARY);
if (fd < 0) {
ux_info("unable to open %s", program->filename);
if (!allow_missing) {
ux_info("...failing\n");
return -1;
}
ux_info("...ignoring\n");
continue;
}
if (!program->sparse) {
ret = apply(qdl, program, fd);
} else {
program_sparse = program_load_sparse(program, fd);
if (!program_sparse) {
ux_err("[PROGRAM] load sparse failed\n");
return -EINVAL;
}
for (; program_sparse; program_sparse = program_sparse->next) {
ret = apply(qdl, program_sparse, fd);
if (ret)
break;
}
ux_err("unable to open %s\n", program->filename);
return -1;
}
ret = apply(qdl, program, fd);
close(fd);
if (ret)
return ret;
@@ -318,7 +295,7 @@ int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, s
struct program *program;
int ret;
for (program = programes; program; program = program->next) {
list_for_each_entry(program, &programs, node) {
if (!program->is_erase)
continue;
@@ -335,7 +312,7 @@ static struct program *program_find_partition(const char *partition)
struct program *program;
const char *label;
for (program = programes; program; program = program->next) {
list_for_each_entry(program, &programs, node) {
label = program->label;
if (!label)
continue;
@@ -409,16 +386,74 @@ int program_is_sec_partition_flashed(void)
void free_programs(void)
{
struct program *program = programes;
struct program *program;
struct program *next;
for (program = programes; program; program = next) {
next = program->next;
list_for_each_entry_safe(program, next, &programs, node) {
free((void *)program->filename);
free((void *)program->label);
free((void *)program->start_sector);
free((void *)program->gpt_partition);
free(program);
}
programes = NULL;
list_init(&programs);
}
int program_cmd_add(const char *address, const char *filename)
{
unsigned int start_sector;
unsigned int num_sectors;
struct program *program;
char *gpt_partition;
int partition;
char buf[20];
int ret;
ret = parse_storage_address(address, &partition, &start_sector, &num_sectors, &gpt_partition);
if (ret < 0)
return ret;
program = calloc(1, sizeof(struct program));
program->sector_size = 0;
program->file_offset = 0;
program->filename = filename ? strdup(filename) : NULL;
program->label = filename ? strdup(filename) : NULL;
program->num_sectors = num_sectors;
program->partition = partition;
program->sparse = false;
sprintf(buf, "%u", start_sector);
program->start_sector = strdup(buf);
program->last_sector = 0;
program->is_nand = false;
program->is_erase = false;
program->gpt_partition = gpt_partition;
list_add(&programs, &program->node);
return 0;
}
int program_resolve_gpt_deferrals(struct qdl_device *qdl)
{
struct program *program;
unsigned int start_sector;
char buf[20];
int ret;
list_for_each_entry(program, &programs, node) {
if (!program->gpt_partition)
continue;
ret = gpt_find_by_name(qdl, program->gpt_partition, &program->partition,
&start_sector, &program->num_sectors);
if (ret < 0)
return -1;
sprintf(buf, "%u", start_sector);
program->start_sector = strdup(buf);
}
return 0;
}

View File

@@ -5,7 +5,7 @@
#include <sys/types.h>
#include <stdbool.h>
#include <stdint.h>
#include "qdl.h"
#include "list.h"
struct program {
unsigned int pages_per_block;
@@ -14,7 +14,7 @@ struct program {
const char *filename;
const char *label;
unsigned int num_sectors;
unsigned int partition;
int partition;
bool sparse;
const char *start_sector;
unsigned int last_sector;
@@ -26,15 +26,20 @@ struct program {
uint32_t sparse_fill_value;
off_t sparse_offset;
struct program *next;
const char *gpt_partition;
struct list_head node;
};
int program_load(const char *program_file, bool is_nand);
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
const char *incdir, bool allow_missing);
struct qdl_device;
int program_load(const char *program_file, bool is_nand, bool allow_missing, const char *incdir);
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd));
int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program));
int program_find_bootable_partition(bool *multiple_found);
int program_is_sec_partition_flashed(void);
int program_cmd_add(const char *address, const char *filename);
int program_resolve_gpt_deferrals(struct qdl_device *qdl);
void free_programs(void);

529
qdl.c

File diff suppressed because it is too large Load Diff

64
qdl.h
View File

@@ -2,6 +2,13 @@
#ifndef __QDL_H__
#define __QDL_H__
#ifdef _WIN32
#include <malloc.h>
#define alloca _alloca
#else
#include <alloca.h>
#endif
#include <stdbool.h>
#include "patch.h"
@@ -26,31 +33,57 @@
(_x + _a - 1) & ~(_a - 1); \
})
#define __unused __attribute__((__unused__))
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define ALIGN_UP(p, size) ({ \
__typeof__(size) _mask = (size) - 1; \
(__typeof__(p))(((uintptr_t)(p) + _mask) & ~_mask); \
})
#define MAPPING_SZ 64
#define SAHARA_ID_EHOSTDL_IMG 13
enum QDL_DEVICE_TYPE {
QDL_DEVICE_USB,
QDL_DEVICE_SIM,
};
enum qdl_storage_type {
QDL_STORAGE_UNKNOWN,
QDL_STORAGE_EMMC,
QDL_STORAGE_NAND,
QDL_STORAGE_UFS,
QDL_STORAGE_NVME,
QDL_STORAGE_SPINOR,
};
struct qdl_device {
enum QDL_DEVICE_TYPE dev_type;
int fd;
size_t max_payload_size;
size_t sector_size;
enum qdl_storage_type storage_type;
unsigned int slot;
int (*open)(struct qdl_device *qdl, const char *serial);
int (*read)(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout);
int (*write)(struct qdl_device *qdl, const void *buf, size_t nbytes);
int (*write)(struct qdl_device *qdl, const void *buf, size_t nbytes, unsigned int timeout);
void (*close)(struct qdl_device *qdl);
void (*set_out_chunk_size)(struct qdl_device *qdl, long size);
void (*set_vip_transfer)(struct qdl_device *qdl, const char *signed_table,
const char *chained_table);
char *mappings[MAPPING_SZ]; // array index is the id from the device
struct vip_transfer_data vip_data;
};
struct sahara_image {
char *name;
void *ptr;
size_t len;
};
struct libusb_device_handle;
struct qdl_device *qdl_init(enum QDL_DEVICE_TYPE type);
@@ -58,16 +91,29 @@ void qdl_deinit(struct qdl_device *qdl);
int qdl_open(struct qdl_device *qdl, const char *serial);
void qdl_close(struct qdl_device *qdl);
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout);
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len);
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len, unsigned int timeout);
void qdl_set_out_chunk_size(struct qdl_device *qdl, long size);
int qdl_vip_transfer_enable(struct qdl_device *qdl, const char *vip_table_path);
struct qdl_device *usb_init(void);
struct qdl_device *sim_init(void);
int firehose_run(struct qdl_device *qdl, const char *incdir, const char *storage, bool allow_missing);
int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
const char *ramdump_path, const char *ramdump_filter);
struct qdl_device_desc {
int vid;
int pid;
char serial[16];
};
struct qdl_device_desc *usb_list(unsigned int *devices_found);
int firehose_run(struct qdl_device *qdl);
int firehose_provision(struct qdl_device *qdl);
int firehose_read_buf(struct qdl_device *qdl, struct read_op *read_op, void *out_buf, size_t out_size);
int sahara_run(struct qdl_device *qdl, const struct sahara_image *images,
const char *ramdump_path,
const char *ramdump_filter);
int load_sahara_image(const char *filename, struct sahara_image *image);
void sahara_images_free(struct sahara_image *images, size_t count);
void print_hex_dump(const char *prefix, const void *buf, size_t len);
unsigned int attr_as_unsigned(xmlNode *node, const char *attr, int *errors);
const char *attr_as_string(xmlNode *node, const char *attr, int *errors);
@@ -82,6 +128,10 @@ void ux_progress(const char *fmt, unsigned int value, unsigned int size, ...);
void print_version(void);
int parse_storage_address(const char *address, int *physical_partition,
unsigned int *start_sector, unsigned int *num_sectors,
char **gpt_partition);
extern bool qdl_debug;
#endif

View File

@@ -12,14 +12,13 @@ const char *__progname = "ramdump";
bool qdl_debug;
static void print_usage(void)
static void print_usage(FILE *out)
{
extern const char *__progname;
fprintf(stderr,
fprintf(out,
"%s [--debug] [-o <ramdump-path>] [segment-filter,...]\n",
__progname);
exit(1);
}
int main(int argc, char **argv)
@@ -41,10 +40,11 @@ int main(int argc, char **argv)
{"version", no_argument, 0, 'v'},
{"output", required_argument, 0, 'o'},
{"serial", required_argument, 0, 'S'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "dvo:S:", options, NULL)) != -1) {
while ((opt = getopt_long(argc, argv, "dvo:S:h", options, NULL)) != -1) {
switch (opt) {
case 'd':
qdl_debug = true;
@@ -59,16 +59,24 @@ int main(int argc, char **argv)
case 'S':
serial = optarg;
break;
case 'h':
print_usage(stdout);
return 0;
default:
print_usage();
print_usage(stderr);
return 1;
}
}
if (optind < argc)
filter = argv[optind++];
if (optind != argc)
print_usage();
if (optind != argc) {
print_usage(stderr);
return 1;
}
ux_init();
if (qdl_debug)
print_version();
@@ -79,7 +87,7 @@ int main(int argc, char **argv)
goto out_cleanup;
}
ret = sahara_run(qdl, NULL, true, ramdump_path, filter);
ret = sahara_run(qdl, NULL, ramdump_path, filter);
if (ret < 0) {
ret = 1;
goto out_cleanup;

94
read.c
View File

@@ -11,20 +11,22 @@
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "list.h"
#include "read.h"
#include "qdl.h"
#include "oscompat.h"
#include "gpt.h"
static struct read_op *read_ops;
static struct read_op *read_ops_last;
static struct list_head read_ops = LIST_INIT(read_ops);
int read_op_load(const char *read_op_file)
int read_op_load(const char *read_op_file, const char *incdir)
{
struct read_op *read_op;
xmlNode *node;
xmlNode *root;
xmlDoc *doc;
int errors;
char tmp[PATH_MAX];
doc = xmlReadFile(read_op_file, NULL, 0);
if (!doc) {
@@ -59,13 +61,13 @@ int read_op_load(const char *read_op_file)
continue;
}
if (read_ops) {
read_ops_last->next = read_op;
read_ops_last = read_op;
} else {
read_ops = read_op;
read_ops_last = read_op;
if (incdir) {
snprintf(tmp, PATH_MAX, "%s/%s", incdir, read_op->filename);
if (access(tmp, F_OK) != -1)
read_op->filename = strdup(tmp);
}
list_add(&read_ops, &read_op->node);
}
xmlFreeDoc(doc);
@@ -73,25 +75,14 @@ int read_op_load(const char *read_op_file)
return 0;
}
int read_op_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd),
const char *incdir)
int read_op_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd))
{
struct read_op *read_op;
const char *filename;
char tmp[PATH_MAX];
int ret;
int fd;
for (read_op = read_ops; read_op; read_op = read_op->next) {
filename = read_op->filename;
if (incdir) {
snprintf(tmp, PATH_MAX, "%s/%s", incdir, filename);
if (access(tmp, F_OK) != -1)
filename = tmp;
}
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
list_for_each_entry(read_op, &read_ops, node) {
fd = open(read_op->filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
if (fd < 0) {
ux_info("unable to open %s...\n", read_op->filename);
return ret;
@@ -106,3 +97,60 @@ int read_op_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
return 0;
}
int read_cmd_add(const char *address, const char *filename)
{
unsigned int start_sector;
unsigned int num_sectors;
struct read_op *read_op;
char *gpt_partition;
int partition;
char buf[20];
int ret;
ret = parse_storage_address(address, &partition, &start_sector, &num_sectors, &gpt_partition);
if (ret < 0)
return ret;
if (num_sectors == 0 && !gpt_partition) {
ux_err("read command without length specifier not supported\n");
return -1;
}
read_op = calloc(1, sizeof(struct read_op));
read_op->sector_size = 0;
read_op->filename = strdup(filename);
read_op->partition = partition;
read_op->num_sectors = num_sectors;
sprintf(buf, "%u", start_sector);
read_op->start_sector = strdup(buf);
read_op->gpt_partition = gpt_partition;
list_add(&read_ops, &read_op->node);
return 0;
}
int read_resolve_gpt_deferrals(struct qdl_device *qdl)
{
struct read_op *read_op;
unsigned int start_sector;
char buf[20];
int ret;
list_for_each_entry(read_op, &read_ops, node) {
if (!read_op->gpt_partition)
continue;
ret = gpt_find_by_name(qdl, read_op->gpt_partition, &read_op->partition,
&start_sector, &read_op->num_sectors);
if (ret < 0)
return -1;
sprintf(buf, "%u", start_sector);
read_op->start_sector = strdup(buf);
}
return 0;
}

15
read.h
View File

@@ -4,20 +4,25 @@
#include <stdbool.h>
#include "list.h"
struct qdl_device;
struct read_op {
unsigned int sector_size;
const char *filename;
unsigned int partition;
int partition;
unsigned int num_sectors;
const char *start_sector;
struct read_op *next;
const char *gpt_partition;
struct list_head node;
};
int read_op_load(const char *read_op_file);
int read_op_load(const char *read_op_file, const char *incdir);
int read_op_execute(struct qdl_device *qdl,
int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd),
const char *incdir);
int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd));
int read_cmd_add(const char *source, const char *filename);
int read_resolve_gpt_deferrals(struct qdl_device *qdl);
#endif

192
sahara.c
View File

@@ -58,7 +58,9 @@
#define SAHARA_DONE_RESP_LENGTH 0xc
#define SAHARA_RESET_LENGTH 0x8
#define DEBUG_BLOCK_SIZE (512 * 1024)
#define DEBUG_BLOCK_SIZE (512u * 1024u)
#define SAHARA_CMD_TIMEOUT_MS 1000
struct sahara_pkt {
uint32_t cmd;
@@ -120,7 +122,7 @@ static void sahara_send_reset(struct qdl_device *qdl)
resp.cmd = SAHARA_RESET_CMD;
resp.length = SAHARA_RESET_LENGTH;
qdl_write(qdl, &resp, resp.length);
qdl_write(qdl, &resp, resp.length, SAHARA_CMD_TIMEOUT_MS);
}
static void sahara_hello(struct qdl_device *qdl, struct sahara_pkt *pkt)
@@ -139,109 +141,79 @@ static void sahara_hello(struct qdl_device *qdl, struct sahara_pkt *pkt)
resp.hello_resp.status = SAHARA_SUCCESS;
resp.hello_resp.mode = pkt->hello_req.mode;
qdl_write(qdl, &resp, resp.length);
qdl_write(qdl, &resp, resp.length, SAHARA_CMD_TIMEOUT_MS);
}
static int sahara_read_common(struct qdl_device *qdl, int progfd, off_t offset, size_t len)
static void sahara_read(struct qdl_device *qdl, struct sahara_pkt *pkt,
const struct sahara_image *images)
{
ssize_t n;
void *buf;
int ret = 0;
buf = malloc(len);
if (!buf)
return -ENOMEM;
lseek(progfd, offset, SEEK_SET);
n = read(progfd, buf, len);
if (n != len) {
ret = -errno;
goto out;
}
n = qdl_write(qdl, buf, n);
if (n != len)
err(1, "failed to write %zu bytes to sahara", len);
free(buf);
out:
return ret;
}
static void sahara_read(struct qdl_device *qdl, struct sahara_pkt *pkt, char *img_arr[], bool single_image)
{
unsigned int image;
const struct sahara_image *image;
unsigned int image_idx;
size_t offset;
size_t len;
int ret;
int fd;
assert(pkt->length == SAHARA_READ_DATA_LENGTH);
ux_debug("READ image: %d offset: 0x%x length: 0x%x\n",
pkt->read_req.image, pkt->read_req.offset, pkt->read_req.length);
if (single_image)
image = 0;
else
image = pkt->read_req.image;
if (image >= MAPPING_SZ || !img_arr[image]) {
ux_err("device requested invalid image: %u\n", image);
image_idx = pkt->read_req.image;
if (image_idx >= MAPPING_SZ || !images[image_idx].ptr) {
ux_err("device requested unknown image id %u, ensure that all Sahara images are provided\n",
image_idx);
sahara_send_reset(qdl);
return;
}
fd = open(img_arr[image], O_RDONLY | O_BINARY);
if (fd < 0) {
ux_err("Can not open \"%s\": %s\n", img_arr[image], strerror(errno));
// Maybe this read was optional. Notify device of error and let
// it decide how to proceed.
sahara_send_reset(qdl);
offset = pkt->read_req.offset;
len = pkt->read_req.length;
image = &images[image_idx];
if (offset > image->len || offset + len > image->len) {
ux_err("device requested invalid range of image %d\n", image_idx);
return;
}
ret = sahara_read_common(qdl, fd, pkt->read_req.offset, pkt->read_req.length);
if (ret < 0)
errx(1, "failed to read image chunk to sahara");
close(fd);
ret = qdl_write(qdl, image->ptr + offset, len, SAHARA_CMD_TIMEOUT_MS);
if (ret < 0 || ((size_t)ret != len))
err(1, "failed to write %zu bytes to sahara", len);
}
static void sahara_read64(struct qdl_device *qdl, struct sahara_pkt *pkt, char *img_arr[], bool single_image)
static void sahara_read64(struct qdl_device *qdl, struct sahara_pkt *pkt,
const struct sahara_image *images)
{
unsigned int image;
const struct sahara_image *image;
unsigned int image_idx;
size_t offset;
size_t len;
int ret;
int fd;
assert(pkt->length == SAHARA_READ_DATA64_LENGTH);
ux_debug("READ64 image: %" PRId64 " offset: 0x%" PRIx64 " length: 0x%" PRIx64 "\n",
pkt->read64_req.image, pkt->read64_req.offset, pkt->read64_req.length);
if (single_image)
image = 0;
else
image = pkt->read64_req.image;
if (image >= MAPPING_SZ || !img_arr[image]) {
ux_err("device requested invalid image: %u\n", image);
sahara_send_reset(qdl);
return;
}
fd = open(img_arr[image], O_RDONLY | O_BINARY);
if (fd < 0) {
ux_err("Can not open \"%s\": %s\n", img_arr[image], strerror(errno));
// Maybe this read was optional. Notify device of error and let
// it decide how to proceed.
image_idx = pkt->read64_req.image;
if (image_idx >= MAPPING_SZ || !images[image_idx].ptr) {
ux_err("device requested unknown image id %u, ensure that all Sahara images are provided\n",
image_idx);
sahara_send_reset(qdl);
return;
}
ret = sahara_read_common(qdl, fd, pkt->read64_req.offset, pkt->read64_req.length);
if (ret < 0)
errx(1, "failed to read image chunk to sahara");
offset = pkt->read64_req.offset;
len = pkt->read64_req.length;
close(fd);
image = &images[image_idx];
if (offset > image->len || offset + len > image->len) {
ux_err("device requested invalid range of image %d\n", image_idx);
return;
}
ret = qdl_write(qdl, image->ptr + offset, len, SAHARA_CMD_TIMEOUT_MS);
if (ret < 0 || ((size_t)ret != len))
err(1, "failed to write %zu bytes to sahara", len);
}
static void sahara_eoi(struct qdl_device *qdl, struct sahara_pkt *pkt)
@@ -259,10 +231,10 @@ static void sahara_eoi(struct qdl_device *qdl, struct sahara_pkt *pkt)
done.cmd = SAHARA_DONE_CMD;
done.length = SAHARA_DONE_LENGTH;
qdl_write(qdl, &done, done.length);
qdl_write(qdl, &done, done.length, SAHARA_CMD_TIMEOUT_MS);
}
static int sahara_done(struct qdl_device *qdl, struct sahara_pkt *pkt)
static int sahara_done(struct qdl_device *qdl __unused, struct sahara_pkt *pkt)
{
assert(pkt->length == SAHARA_DONE_RESP_LENGTH);
@@ -303,13 +275,13 @@ static ssize_t sahara_debug64_one(struct qdl_device *qdl,
chunk = 0;
while (chunk < region.length) {
remain = MIN(region.length - chunk, DEBUG_BLOCK_SIZE);
remain = MIN((uint64_t)(region.length - chunk), DEBUG_BLOCK_SIZE);
read_req.cmd = SAHARA_MEM_READ64_CMD;
read_req.length = SAHARA_MEM_READ64_LENGTH;
read_req.debug64_req.addr = region.addr + chunk;
read_req.debug64_req.length = remain;
n = qdl_write(qdl, &read_req, read_req.length);
n = qdl_write(qdl, &read_req, read_req.length, SAHARA_CMD_TIMEOUT_MS);
if (n < 0)
break;
@@ -322,7 +294,7 @@ static ssize_t sahara_debug64_one(struct qdl_device *qdl,
goto out;
}
while (buf_offset < n) {
while (buf_offset < (size_t)n) {
written = write(fd, buf + buf_offset, n - buf_offset);
if (written <= 0) {
warn("failed to write ramdump chunk to \"%s\"", region.filename);
@@ -337,6 +309,8 @@ static ssize_t sahara_debug64_one(struct qdl_device *qdl,
qdl_read(qdl, buf, DEBUG_BLOCK_SIZE, 10);
chunk += DEBUG_BLOCK_SIZE;
ux_progress("%s", chunk, region.length, region.filename);
}
out:
@@ -393,7 +367,7 @@ static void sahara_debug64(struct qdl_device *qdl, struct sahara_pkt *pkt,
struct sahara_debug_region64 *table;
struct sahara_pkt read_req;
ssize_t n;
int i;
size_t i;
assert(pkt->length == SAHARA_MEM_DEBUG64_LENGTH);
@@ -405,19 +379,21 @@ static void sahara_debug64(struct qdl_device *qdl, struct sahara_pkt *pkt,
read_req.debug64_req.addr = pkt->debug64_req.addr;
read_req.debug64_req.length = pkt->debug64_req.length;
n = qdl_write(qdl, &read_req, read_req.length);
n = qdl_write(qdl, &read_req, read_req.length, SAHARA_CMD_TIMEOUT_MS);
if (n < 0)
return;
table = malloc(read_req.debug64_req.length);
n = qdl_read(qdl, table, pkt->debug64_req.length, 1000);
n = qdl_read(qdl, table, pkt->debug64_req.length, SAHARA_CMD_TIMEOUT_MS);
if (n < 0)
return;
for (i = 0; i < pkt->debug64_req.length / sizeof(table[0]); i++) {
if (sahara_debug64_filter(table[i].filename, filter))
if (sahara_debug64_filter(table[i].filename, filter)) {
ux_info("%s skipped per filter\n", table[i].filename);
continue;
}
ux_debug("%-2d: type 0x%" PRIx64 " address: 0x%" PRIx64 " length: 0x%"
PRIx64 " region: %s filename: %s\n",
@@ -427,6 +403,8 @@ static void sahara_debug64(struct qdl_device *qdl, struct sahara_pkt *pkt,
n = sahara_debug64_one(qdl, table[i], ramdump_path);
if (n < 0)
break;
ux_info("%s dumped successfully\n", table[i].filename);
}
free(table);
@@ -434,8 +412,37 @@ static void sahara_debug64(struct qdl_device *qdl, struct sahara_pkt *pkt,
sahara_send_reset(qdl);
}
int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
const char *ramdump_path, const char *ramdump_filter)
static bool sahara_has_done_pending_quirk(const struct sahara_image *images)
{
unsigned int count = 0;
int i;
/*
* E.g MSM8916 EDL reports done = pending, allow this when one a single
* image is provided, and it's used as SAHARA_ID_EHOSTDL_IMG.
*/
for (i = 0; i < MAPPING_SZ; i++) {
if (images[i].ptr)
count++;
}
return count == 1 && images[SAHARA_ID_EHOSTDL_IMG].ptr;
}
static void sahara_debug_list_images(const struct sahara_image *images)
{
int i;
ux_debug("Sahara images:\n");
for (i = 0; i < MAPPING_SZ; i++) {
if (images[i].ptr)
ux_debug(" %2d: %s\n", i, images[i].name ? : "(unknown)");
}
}
int sahara_run(struct qdl_device *qdl, const struct sahara_image *images,
const char *ramdump_path,
const char *ramdump_filter)
{
struct sahara_pkt *pkt;
char buf[4096];
@@ -443,6 +450,9 @@ int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
bool done = false;
int n;
if (images)
sahara_debug_list_images(images);
/*
* Don't need to do anything in simulation mode with Sahara,
* we care only about Firehose protocol
@@ -451,12 +461,14 @@ int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
return 0;
while (!done) {
n = qdl_read(qdl, buf, sizeof(buf), 1000);
if (n < 0)
n = qdl_read(qdl, buf, sizeof(buf), SAHARA_CMD_TIMEOUT_MS);
if (n < 0) {
ux_err("failed to read sahara request from device\n");
break;
}
pkt = (struct sahara_pkt *)buf;
if (n != pkt->length) {
if ((uint32_t)n != pkt->length) {
ux_err("request length not matching received request\n");
return -EINVAL;
}
@@ -466,7 +478,7 @@ int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
sahara_hello(qdl, pkt);
break;
case SAHARA_READ_DATA_CMD:
sahara_read(qdl, pkt, img_arr, single_image);
sahara_read(qdl, pkt, images);
break;
case SAHARA_END_OF_IMAGE_CMD:
sahara_eoi(qdl, pkt);
@@ -475,14 +487,14 @@ int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
done = sahara_done(qdl, pkt);
/* E.g MSM8916 EDL reports done = 0 here */
if (single_image)
if (sahara_has_done_pending_quirk(images))
done = true;
break;
case SAHARA_MEM_DEBUG64_CMD:
sahara_debug64(qdl, pkt, ramdump_path, ramdump_filter);
break;
case SAHARA_READ_DATA64_CMD:
sahara_read64(qdl, pkt, img_arr, single_image);
sahara_read64(qdl, pkt, images);
break;
case SAHARA_RESET_RESP_CMD:
assert(pkt->length == SAHARA_RESET_LENGTH);

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