97 Commits
v2.1 ... v2.2

Author SHA1 Message Date
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
43 changed files with 3327 additions and 696 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

View File

@@ -5,28 +5,151 @@ on:
push:
jobs:
build:
build-linux:
strategy:
fail-fast: false
matrix:
os: [ ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, macos-latest ]
runs-on: ${{ matrix.os }}
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
- name: Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev
- name: Build
run: make
- name: Run tests
run: make tests
- 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-13, arch: intel }
runs-on: ${{ matrix.sys }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Dependencies (Ubuntu)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev
- name: Dependencies (macOS)
if: runner.os == 'macOS'
- name: Dependencies
run: |
brew install libxml2
brew install libusb
- name: Build
run: make
- 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
- name: Setup MSYS2
id: msys2
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
install: >
base-devel
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
- name: Build
run: make
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

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

5
.gitignore vendored
View File

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

View File

@@ -1,20 +1,31 @@
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`
ifeq ($(OS),Windows_NT)
LDFLAGS += -lws2_32
endif
prefix := /usr/local
QDL_SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c read.c ufs.c usb.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
QDL_OBJS := $(QDL_SRCS:.c=.o)
RAMDUMP_SRCS := ramdump.c sahara.c usb.c util.c
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
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_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
default: $(QDL) $(RAMDUMP) $(KS_OUT)
$(QDL): $(QDL_OBJS)
@@ -29,12 +40,44 @@ $(KS_OUT): $(KS_OBJS)
compile_commands.json: $(QDL_SRCS) $(KS_SRCS)
@echo -n $^ | jq -snR "[inputs|split(\" \")[]|{directory:\"$(PWD)\", command: \"$(CC) $(CFLAGS) -c \(.)\", file:.}]" > $@
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 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 and libusb-1.0 headers
and libraries, found in e.g. the libxml2-dev and libusb-1.0.0-dev packages
With these installed run:
make

170
README.md Normal file
View File

@@ -0,0 +1,170 @@
# 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
make
```
### MacOS
```bash
brew install libxml2 pkg-config libusb
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 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
```
### 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
```
## Run tests
To run the integration test suite for QDL, use the `make tests` target:
```bash
make tests
```
## 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

49
io.c Normal file
View File

@@ -0,0 +1,49 @@
// 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);
}
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)
{
return qdl->write(qdl, buf, len);
}

26
ks.c
View File

@@ -1,21 +1,24 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <poll.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "qdl.h"
#include "oscompat.h"
#ifdef _WIN32
const char *__progname = "ks";
#endif
static struct qdl_device qdl;
@@ -28,13 +31,13 @@ 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)
{
return write(qdl->fd, buf, len);
}
static void print_usage(void)
{
extern const char *__progname;
fprintf(stderr,
"%s -p <sahara dev_node> -s <id:file path> ...\n",
__progname);
@@ -44,7 +47,7 @@ static void print_usage(void)
"\n"
"One -p instance is required. One or more -s instances are required.\n"
"\n"
"Example: \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");
}
@@ -58,13 +61,21 @@ int main(int argc, char **argv)
int ret;
static struct option options[] = {
{"debug", no_argument, 0, 'd'},
{"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, "p:s:", options, NULL )) != -1) {
while ((opt = getopt_long(argc, argv, "dvp:s:", 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);
@@ -103,6 +114,9 @@ int main(int argc, char **argv)
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);

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

68
patch.c
View File

@@ -1,42 +1,18 @@
// 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;
@@ -50,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;
}
@@ -59,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;
}
@@ -78,7 +54,7 @@ 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;
}
@@ -96,12 +72,19 @@ int patch_load(const char *patch_file)
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 (!strcmp(patch->filename, "DISK"))
count++;
}
for (patch = patches; patch; patch = patch->next) {
if (strcmp(patch->filename, "DISK"))
continue;
@@ -109,7 +92,28 @@ int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, s
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 = patches;
struct patch *next;
for (patch = patches; patch; patch = next) {
next = patch->next;
free((void *)patch->filename);
free((void *)patch->start_sector);
free((void *)patch->value);
free((void *)patch->what);
free(patch);
}
patches = NULL;
}

10
patch.h
View File

@@ -1,14 +1,15 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef __PATCH_H__
#define __PATCH_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;
@@ -18,5 +19,6 @@ struct patch {
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

286
program.c
View File

@@ -1,35 +1,13 @@
// 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.
*/
#define _FILE_OFFSET_BITS 64
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
@@ -38,6 +16,8 @@
#include "program.h"
#include "qdl.h"
#include "oscompat.h"
#include "sparse.h"
static struct program *programes;
static struct program *programes_last;
@@ -47,24 +27,21 @@ static int load_erase_tag(xmlNode *node, bool is_nand)
struct program *program;
int errors = 0;
if (!is_nand) {
fprintf(stderr, "got \"erase\" tag for non-NAND storage\n");
return -EINVAL;
}
program = calloc(1, sizeof(struct program));
program->is_nand = true;
program->is_nand = is_nand;
program->is_erase = true;
program->pages_per_block = attr_as_unsigned(node, "PAGES_PER_BLOCK", &errors);
program->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
program->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
program->start_sector = attr_as_string(node, "start_sector", &errors);
if (is_nand) {
program->pages_per_block = attr_as_unsigned(node, "PAGES_PER_BLOCK", &errors);
}
if (errors) {
fprintf(stderr, "[PROGRAM] errors while parsing erase tag\n");
ux_err("errors while parsing erase tag\n");
free(program);
return -EINVAL;
}
@@ -80,6 +57,125 @@ static int load_erase_tag(xmlNode *node, bool is_nand)
return 0;
}
static struct program *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;
unsigned int start_sector;
uint32_t sparse_fill_value;
uint64_t chunk_size;
off_t sparse_offset;
int chunk_type;
if (sparse_header_parse(fd, &sparse_header)) {
/*
* If the XML tag "program" contains the attribute 'sparse="true"'
* 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.
*/
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;
}
ux_err("[PROGRAM] Unable to parse sparse header at %s...failed\n",
program->filename);
return NULL;
}
for (uint32_t i = 0; i < sparse_header.total_chunks; ++i) {
chunk_type = sparse_chunk_header_parse(fd, &sparse_header,
&chunk_size,
&sparse_fill_value,
&sparse_offset);
if (chunk_type < 0) {
ux_err("[PROGRAM] Unable to parse sparse chunk %i at %s...failed\n",
i, program->filename);
return NULL;
}
if (chunk_size == 0)
continue;
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;
}
if (chunk_size / program->sector_size >= UINT_MAX) {
/*
* Perhaps the programmer can handle larger "num_sectors"?
* Let's cap it for now, it's big enough for now...
*/
ux_err("[SPARSE] File chunk #%u size %" PRIu64 " is too large\n",
i, chunk_size);
return NULL;
}
switch (chunk_type) {
case CHUNK_TYPE_RAW:
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_RAW;
program_sparse->sparse_offset = sparse_offset;
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;
}
start_sector = (unsigned int)strtoul(program->start_sector, NULL, 0);
start_sector += chunk_size / program->sector_size;
sprintf(tmp, "%u", start_sector);
program->start_sector = strdup(tmp);
}
return programes_sparse;
}
static int load_program_tag(xmlNode *node, bool is_nand)
{
struct program *program;
@@ -94,11 +190,12 @@ static int load_program_tag(xmlNode *node, bool is_nand)
program->label = attr_as_string(node, "label", &errors);
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
program->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
program->sparse = attr_as_bool(node, "sparse", &errors);
program->start_sector = attr_as_string(node, "start_sector", &errors);
if (is_nand) {
program->pages_per_block = attr_as_unsigned(node, "PAGES_PER_BLOCK", &errors);
if (NULL != xmlGetProp(node, (xmlChar *)"last_sector")) {
if (xmlGetProp(node, (xmlChar *)"last_sector")) {
program->last_sector = attr_as_unsigned(node, "last_sector", &errors);
}
} else {
@@ -106,7 +203,7 @@ static int load_program_tag(xmlNode *node, bool is_nand)
}
if (errors) {
fprintf(stderr, "[PROGRAM] errors while parsing program\n");
ux_err("errors while parsing program tag\n");
free(program);
return -EINVAL;
}
@@ -131,7 +228,7 @@ int program_load(const char *program_file, bool is_nand)
doc = xmlReadFile(program_file, NULL, 0);
if (!doc) {
fprintf(stderr, "[PROGRAM] failed to parse %s\n", program_file);
ux_err("failed to parse program-type file \"%s\"\n", program_file);
return -EINVAL;
}
@@ -145,7 +242,7 @@ int program_load(const char *program_file, bool is_nand)
else if (!xmlStrcmp(node->name, (xmlChar *)"program"))
errors = load_program_tag(node, is_nand);
else {
fprintf(stderr, "[PROGRAM] unrecognized tag \"%s\"\n", node->name);
ux_err("unrecognized tag \"%s\" in program-type file \"%s\"\n", node->name, program_file);
errors = -EINVAL;
}
@@ -163,6 +260,7 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
const char *incdir, bool allow_missing)
{
struct program *program;
struct program *program_sparse;
const char *filename;
char tmp[PATH_MAX];
int ret;
@@ -179,19 +277,33 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
filename = tmp;
}
fd = open(filename, O_RDONLY);
fd = open(filename, O_RDONLY | O_BINARY);
if (fd < 0) {
printf("Unable to open %s", program->filename);
ux_info("unable to open %s", program->filename);
if (!allow_missing) {
printf("...failing\n");
ux_info("...failing\n");
return -1;
}
printf("...ignoring\n");
ux_info("...ignoring\n");
continue;
}
ret = apply(qdl, program, fd);
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;
}
}
close(fd);
if (ret)
@@ -206,7 +318,6 @@ 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) {
if (!program->is_erase)
continue;
@@ -219,6 +330,23 @@ int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, s
return 0;
}
static struct program *program_find_partition(const char *partition)
{
struct program *program;
const char *label;
for (program = programes; program; program = program->next) {
label = program->label;
if (!label)
continue;
if (!strcmp(label, partition))
return program;
}
return NULL;
}
/**
* program_find_bootable_partition() - find one bootable partition
*
@@ -226,27 +354,71 @@ int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, s
*
* Scan program tags for a partition with the label "sbl1", "xbl" or "xbl_a"
* and return the partition number for this. If more than one line matches
* we're assuming our logic is flawed and return an error.
* we're informing the caller so that they can warn the user about the
* uncertainty of this logic.
*/
int program_find_bootable_partition(void)
int program_find_bootable_partition(bool *multiple_found)
{
struct program *program;
const char *label;
int part = -ENOENT;
for (program = programes; program; program = program->next) {
label = program->label;
if (!label)
continue;
*multiple_found = false;
if (!strcmp(label, "xbl") || !strcmp(label, "xbl_a") ||
!strcmp(label, "sbl1")) {
if (part != -ENOENT)
return -EINVAL;
program = program_find_partition("xbl");
if (program)
part = program->partition;
program = program_find_partition("xbl_a");
if (program) {
if (part != -ENOENT)
*multiple_found = true;
else
part = program->partition;
}
program = program_find_partition("sbl1");
if (program) {
if (part != -ENOENT)
*multiple_found = true;
else
part = program->partition;
}
}
return part;
}
/**
* program_is_sec_partition_flashed() - find if secdata partition is flashed
*
* Returns true if filename for secdata is set in program*.xml,
* or false otherwise.
*/
int program_is_sec_partition_flashed(void)
{
struct program *program;
program = program_find_partition("secdata");
if (!program)
return false;
if (program->filename)
return true;
return false;
}
void free_programs(void)
{
struct program *program = programes;
struct program *next;
for (program = programes; program; program = next) {
next = program->next;
free((void *)program->filename);
free((void *)program->label);
free((void *)program->start_sector);
free(program);
}
programes = NULL;
}

View File

@@ -1,23 +1,31 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef __PROGRAM_H__
#define __PROGRAM_H__
#include <sys/types.h>
#include <stdbool.h>
#include <stdint.h>
#include "qdl.h"
struct program {
unsigned pages_per_block;
unsigned sector_size;
unsigned file_offset;
unsigned int pages_per_block;
unsigned int sector_size;
unsigned int file_offset;
const char *filename;
const char *label;
unsigned num_sectors;
unsigned partition;
unsigned int num_sectors;
unsigned int partition;
bool sparse;
const char *start_sector;
unsigned last_sector;
unsigned int last_sector;
bool is_nand;
bool is_erase;
unsigned int sparse_chunk_type;
uint32_t sparse_fill_value;
off_t sparse_offset;
struct program *next;
};
@@ -25,6 +33,9 @@ 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);
int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program));
int program_find_bootable_partition(void);
int program_find_bootable_partition(bool *multiple_found);
int program_is_sec_partition_flashed(void);
void free_programs(void);
#endif

186
qdl.c
View File

@@ -1,35 +1,9 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2016-2017, Linaro Ltd.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
* 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 <err.h>
#include <errno.h>
#include <getopt.h>
#include <stdbool.h>
@@ -41,9 +15,16 @@
#include "qdl.h"
#include "patch.h"
#include "program.h"
#include "ufs.h"
#include "oscompat.h"
#include "vip.h"
#define MAX_USBFS_BULK_SIZE (16*1024)
#ifdef _WIN32
const char *__progname = "qdl";
#endif
#define MAX_USBFS_BULK_SIZE (16 * 1024)
enum {
QDL_FILE_UNKNOWN,
@@ -55,7 +36,6 @@ enum {
};
bool qdl_debug;
static struct qdl_device qdl;
static int detect_type(const char *xml_file)
{
@@ -66,31 +46,31 @@ static int detect_type(const char *xml_file)
doc = xmlReadFile(xml_file, NULL, 0);
if (!doc) {
fprintf(stderr, "[PATCH] failed to parse %s\n", xml_file);
ux_err("failed to parse XML file \"%s\"\n", xml_file);
return -EINVAL;
}
root = xmlDocGetRootElement(doc);
if (!xmlStrcmp(root->name, (xmlChar*)"patches")) {
if (!xmlStrcmp(root->name, (xmlChar *)"patches")) {
type = QDL_FILE_PATCH;
} else if (!xmlStrcmp(root->name, (xmlChar*)"data")) {
} else if (!xmlStrcmp(root->name, (xmlChar *)"data")) {
for (node = root->children; node ; node = node->next) {
if (node->type != XML_ELEMENT_NODE)
continue;
if (!xmlStrcmp(node->name, (xmlChar*)"program")) {
if (!xmlStrcmp(node->name, (xmlChar *)"program")) {
type = QDL_FILE_PROGRAM;
break;
}
if (!xmlStrcmp(node->name, (xmlChar*)"read")) {
if (!xmlStrcmp(node->name, (xmlChar *)"read")) {
type = QDL_FILE_READ;
break;
}
if (!xmlStrcmp(node->name, (xmlChar*)"ufs")) {
if (!xmlStrcmp(node->name, (xmlChar *)"ufs")) {
type = QDL_FILE_UFS;
break;
}
}
} else if (!xmlStrcmp(root->name, (xmlChar*)"contents")) {
} else if (!xmlStrcmp(root->name, (xmlChar *)"contents")) {
type = QDL_FILE_CONTENTS;
}
@@ -99,46 +79,77 @@ static int detect_type(const char *xml_file)
return type;
}
static void print_usage(void)
static void print_usage(FILE *out)
{
extern const char *__progname;
fprintf(stderr,
"%s [--debug] [--allow-missing] [--storage <emmc|nand|ufs>] [--finalize-provisioning] [--include <PATH>] [--serial <NUM>] [--out-chunk-size <SIZE>] <prog.mbn> [<program> <patch> ...]\n",
__progname);
}
enum {
OPT_OUT_CHUNK_SIZE = 1000,
};
fprintf(out, "Usage: %s [options] <prog.mbn> [<program> <patch> ...]\n", __progname);
fprintf(out, " -d, --debug\t\t\tPrint detailed debug info\n");
fprintf(out, " -v, --version\t\t\tPrint the current version and exit\n");
fprintf(out, " -n, --dry-run\t\t\tDry run execution, no device reading or flashing\n");
fprintf(out, " -f, --allow-missing\t\tAllow skipping of missing files during flashing\n");
fprintf(out, " -s, --storage=T\t\tSet target storage type T: <emmc|nand|ufs>\n");
fprintf(out, " -l, --finalize-provisioning\tProvision the target storage\n");
fprintf(out, " -i, --include=T\t\tSet an optional folder T to search for files\n");
fprintf(out, " -S, --serial=T\t\t\tSelect target by serial number T (e.g. <0AA94EFD>)\n");
fprintf(out, " -u, --out-chunk-size=T\t\tOverride chunk size for transaction with T\n");
fprintf(out, " -t, --create-digests=T\t\tGenerate table of digests in the T folder\n");
fprintf(out, " -D, --vip-table-path=T\t\tUse digest tables in the T folder for VIP\n");
fprintf(out, " -h, --help\t\t\tPrint this usage info\n");
fprintf(out, "\n");
fprintf(out, "Example: %s prog_firehose_ddr.elf rawprogram*.xml patch*.xml\n", __progname);
}
int main(int argc, char **argv)
{
char *prog_mbn, *storage="ufs";
char *prog_mbn, *storage = "ufs";
char *incdir = NULL;
char *serial = NULL;
const char *vip_generate_dir = NULL;
const char *vip_table_path = NULL;
int type;
int ret;
int opt;
bool qdl_finalize_provisioning = false;
bool allow_fusing = false;
bool allow_missing = false;
long out_chunk_size;
long out_chunk_size = 0;
struct qdl_device *qdl = NULL;
enum QDL_DEVICE_TYPE qdl_dev_type = QDL_DEVICE_USB;
static struct option options[] = {
{"debug", no_argument, 0, 'd'},
{"version", no_argument, 0, 'v'},
{"include", required_argument, 0, 'i'},
{"finalize-provisioning", no_argument, 0, 'l'},
{"out-chunk-size", required_argument, 0, OPT_OUT_CHUNK_SIZE },
{"out-chunk-size", required_argument, 0, 'u' },
{"serial", required_argument, 0, 'S'},
{"vip-table-path", required_argument, 0, 'D'},
{"storage", required_argument, 0, 's'},
{"allow-missing", no_argument, 0, 'f'},
{"allow-fusing", no_argument, 0, 'c'},
{"dry-run", no_argument, 0, 'n'},
{"create-digests", required_argument, 0, 't'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "dfi:S:", options, NULL )) != -1) {
while ((opt = getopt_long(argc, argv, "dvi:lu:S:D:s:fcnt:h", options, NULL)) != -1) {
switch (opt) {
case 'd':
qdl_debug = true;
break;
case 'n':
qdl_dev_type = QDL_DEVICE_SIM;
break;
case 't':
vip_generate_dir = optarg;
/* we also enforce dry-run mode */
qdl_dev_type = QDL_DEVICE_SIM;
break;
case 'v':
print_version();
return 0;
case 'f':
allow_missing = true;
break;
@@ -148,9 +159,11 @@ int main(int argc, char **argv)
case 'l':
qdl_finalize_provisioning = true;
break;
case OPT_OUT_CHUNK_SIZE:
case 'c':
allow_fusing = true;
break;
case 'u':
out_chunk_size = strtol(optarg, NULL, 10);
qdl_set_out_chunk_size(&qdl, out_chunk_size);
break;
case 's':
storage = optarg;
@@ -158,18 +171,52 @@ int main(int argc, char **argv)
case 'S':
serial = optarg;
break;
case 'D':
vip_table_path = optarg;
break;
case 'h':
print_usage(stdout);
return 0;
default:
print_usage();
print_usage(stderr);
return 1;
}
}
/* at least 2 non optional args required */
if ((optind + 2) > argc) {
print_usage();
print_usage(stderr);
return 1;
}
qdl = qdl_init(qdl_dev_type);
if (!qdl) {
ret = -1;
goto out_cleanup;
}
if (vip_table_path) {
if (vip_generate_dir)
errx(1, "VIP mode and VIP table generation can't be enabled together\n");
ret = vip_transfer_init(qdl, vip_table_path);
if (ret)
errx(1, "VIP initialization failed\n");
}
if (out_chunk_size)
qdl_set_out_chunk_size(qdl, out_chunk_size);
if (vip_generate_dir) {
ret = vip_gen_init(qdl, vip_generate_dir);
if (ret)
goto out_cleanup;
}
ux_init();
if (qdl_debug)
print_version();
prog_mbn = argv[optind++];
do {
@@ -187,6 +234,10 @@ int main(int argc, char **argv)
ret = program_load(argv[optind], !strcmp(storage, "nand"));
if (ret < 0)
errx(1, "program_load %s failed", argv[optind]);
if (!allow_fusing && program_is_sec_partition_flashed())
errx(1, "secdata partition to be programmed, which can lead to irreversible"
" changes. Allow explicitly with --allow-fusing parameter");
break;
case QDL_FILE_READ:
ret = read_op_load(argv[optind]);
@@ -194,7 +245,7 @@ int main(int argc, char **argv)
errx(1, "read_op_load %s failed", argv[optind]);
break;
case QDL_FILE_UFS:
ret = ufs_load(argv[optind],qdl_finalize_provisioning);
ret = ufs_load(argv[optind], qdl_finalize_provisioning);
if (ret < 0)
errx(1, "ufs_load %s failed", argv[optind]);
break;
@@ -204,18 +255,31 @@ int main(int argc, char **argv)
}
} while (++optind < argc);
ret = qdl_open(&qdl, serial);
ret = qdl_open(qdl, serial);
if (ret)
return 1;
goto out_cleanup;
qdl.mappings[0] = prog_mbn;
ret = sahara_run(&qdl, qdl.mappings, true, NULL, NULL);
qdl->mappings[0] = prog_mbn;
ret = sahara_run(qdl, qdl->mappings, true, NULL, NULL);
if (ret < 0)
return 1;
goto out_cleanup;
ret = firehose_run(&qdl, incdir, storage, allow_missing);
ret = firehose_run(qdl, incdir, storage, allow_missing);
if (ret < 0)
return 1;
goto out_cleanup;
return 0;
out_cleanup:
if (vip_generate_dir)
vip_gen_finalize(qdl);
qdl_close(qdl);
free_programs();
free_patches();
if (qdl->vip_data.state != VIP_DISABLED)
vip_transfer_deinit(qdl);
qdl_deinit(qdl);
return !!ret;
}

74
qdl.h
View File

@@ -1,3 +1,4 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef __QDL_H__
#define __QDL_H__
@@ -7,36 +8,79 @@
#include "program.h"
#include "read.h"
#include <libxml/tree.h>
#include "vip.h"
#define container_of(ptr, typecast, member) ({ \
void *_ptr = (void *)(ptr); \
((typeof(typecast) *)(_ptr - offsetof(typecast, member))); })
#define MIN(x, y) ({ \
__typeof__(x) _x = (x); \
__typeof__(y) _y = (y); \
_x < _y ? _x : _y; \
})
#define ROUND_UP(x, a) ({ \
__typeof__(x) _x = (x); \
__typeof__(a) _a = (a); \
(_x + _a - 1) & ~(_a - 1); \
})
#define MAPPING_SZ 64
struct libusb_device_handle;
struct qdl_device {
struct libusb_device_handle *usb_handle;
int fd;
int in_ep;
int out_ep;
size_t in_maxpktsize;
size_t out_maxpktsize;
size_t out_chunk_size;
char *mappings[MAPPING_SZ]; // array index is the id from the device
enum QDL_DEVICE_TYPE {
QDL_DEVICE_USB,
QDL_DEVICE_SIM,
};
struct qdl_device {
enum QDL_DEVICE_TYPE dev_type;
int fd;
size_t max_payload_size;
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);
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 libusb_device_handle;
struct qdl_device *qdl_init(enum QDL_DEVICE_TYPE type);
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);
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);
void print_hex_dump(const char *prefix, const void *buf, size_t len);
unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors);
unsigned int attr_as_unsigned(xmlNode *node, const char *attr, int *errors);
const char *attr_as_string(xmlNode *node, const char *attr, int *errors);
bool attr_as_bool(xmlNode *node, const char *attr, int *errors);
void ux_init(void);
void ux_err(const char *fmt, ...);
void ux_info(const char *fmt, ...);
void ux_log(const char *fmt, ...);
void ux_debug(const char *fmt, ...);
void ux_progress(const char *fmt, unsigned int value, unsigned int size, ...);
void print_version(void);
extern bool qdl_debug;

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: BSD-3-Clause
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
@@ -5,11 +6,16 @@
#include "qdl.h"
#ifdef _WIN32
const char *__progname = "ramdump";
#endif
bool qdl_debug;
static void print_usage(void)
{
extern const char *__progname;
fprintf(stderr,
"%s [--debug] [-o <ramdump-path>] [segment-filter,...]\n",
__progname);
@@ -18,25 +24,35 @@ static void print_usage(void)
int main(int argc, char **argv)
{
struct qdl_device qdl;
struct qdl_device *qdl;
qdl = qdl_init(QDL_DEVICE_USB);
if (!qdl)
return 1;
char *ramdump_path = ".";
char *filter = NULL;
char *serial = NULL;
int ret;
int ret = 0;
int opt;
static struct option options[] = {
{"debug", no_argument, 0, 'd'},
{"version", no_argument, 0, 'v'},
{"output", required_argument, 0, 'o'},
{"serial", required_argument, 0, 'S'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "do:S:", options, NULL )) != -1) {
while ((opt = getopt_long(argc, argv, "dvo:S:", options, NULL)) != -1) {
switch (opt) {
case 'd':
qdl_debug = true;
break;
case 'v':
print_version();
ret = 0;
goto out_cleanup;
case 'o':
ramdump_path = optarg;
break;
@@ -54,13 +70,24 @@ int main(int argc, char **argv)
if (optind != argc)
print_usage();
ret = qdl_open(&qdl, serial);
if (ret)
return 1;
if (qdl_debug)
print_version();
ret = sahara_run(&qdl, NULL, true, ramdump_path, filter);
if (ret < 0)
return 1;
ret = qdl_open(qdl, serial);
if (ret) {
ret = 1;
goto out_cleanup;
}
return 0;
ret = sahara_run(qdl, NULL, true, ramdump_path, filter);
if (ret < 0) {
ret = 1;
goto out_cleanup;
}
out_cleanup:
qdl_close(qdl);
qdl_deinit(qdl);
return ret;
}

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