152 Commits
nbdkit ... 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
Nicolas Dechesne
ad13228e6a ufs: add support for UFS WriteBooster feature
WriteBooster is a new feature added in UFS3.1. There are new
paramaters in the UFS header config. Trying to provision a 3.1+ device
without these parameters fail.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

CodeQL reports:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Add the relevant files to .gitignore.

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

Introduce support for this part of the protocol.

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

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

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

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

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

A sample invocation -

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

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

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

To support this, we need several changes.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2021-04-15 12:00:44 -05:00
48 changed files with 4516 additions and 1833 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

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

@@ -0,0 +1,155 @@
name: Buildtest
on:
pull_request:
push:
jobs:
build-linux:
strategy:
fail-fast: false
matrix:
include:
- { arch: x64, os: 24, runner: ubuntu-24.04 }
- { arch: arm64, os: 24, runner: ubuntu-24.04-arm }
- { arch: x64, os: 22, runner: ubuntu-22.04 }
- { arch: arm64, os: 22, runner: ubuntu-22.04-arm }
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout
uses: actions/checkout@v4
- 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
run: |
brew install libxml2
- 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

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

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

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

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

9
.gitignore vendored
View File

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

View File

@@ -1,33 +1,83 @@
OUT := qdl
NBDKIT := nbdkit-qdl-plugin.so
QDL := qdl
RAMDUMP := qdl-ramdump
VERSION := $(or $(VERSION), $(shell git describe --dirty --always --tags 2>/dev/null), "unknown-version")
CFLAGS := -O2 -Wall -g `xml2-config --cflags` -fPIC
LDFLAGS := `xml2-config --libs` -ludev
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
COMMON_SRCS := firehose.c json.c sahara.c util.c patch.c program.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)
QDL_SRCS := qdl.c
QDL_OBJS := $(COMMON_SRCS:.c=.o) $(QDL_SRCS:.c=.o)
RAMDUMP_SRCS := ramdump.c sahara.c io.c sim.c usb.c util.c ux.c oscompat.c
RAMDUMP_OBJS := $(RAMDUMP_SRCS:.c=.o)
$(OUT): $(QDL_OBJS)
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_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)
$(CC) -o $@ $^ $(LDFLAGS)
NBDKIT_SRCS := nbdkit-qdl-plugin.c
NBDKIT_OBJS := $(COMMON_SRCS:.c=.o) $(NBDKIT_SRCS:.c=.o)
$(NBDKIT): LDFLAGS += -shared
$(NBDKIT): $(NBDKIT_OBJS)
$(RAMDUMP): $(RAMDUMP_OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
.PHONY: lib
lib: $(NBDKIT)
$(KS_OUT): $(KS_OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
.PHONY: all
all: $(OUT) $(NBDKIT)
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 $(OUT) $(QDL_OBJS) $(NBDKIT_OBJS)
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: $(OUT)
install -D -m 755 $< $(DESTDIR)$(prefix)/bin/$<
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 -

43
README
View File

@@ -1,43 +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> ...]
nbdkit plugin
=============
In addition to the qdl programmer a nbdkit plugin can be built. This provides
block layer access to the flash on the device, allowing partition tables to be
modified and partitions mounted on the host PC.
The nbdkit plugin has the following configuration parameters:
programmer: firehose programmer
lun: LUN number to be managed, defaults to 1
storage: "ufs" or "emmc" to select firehose configuration
To launch a nbd server, launch nbdkit as follows:
modprobe nbd
nbdkit -fv ./nbdkit-qdl-plugin.so programmer=prog_firehose_ddr.elf lun=0
Then connect nbd to this, using:
sudo nbd-client -b 4096 localhost /dev/nbd0
After this /dev/nbd0 and /dev/nbd0p* can be used to access the full storage
device, as well as the individual partitions on the device.
To disconnect the nbd client, unmount any partitions and run:
sudo nbd-client -d /dev/nbd0
Building
========
In order to build the project you need libxml2 headers and libraries, found in
e.g. the libxml2-dev package.
With this installed run:
make
or to build the nbdkit plugin run:
make lib

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);
}

451
json.c
View File

@@ -1,451 +0,0 @@
/*
* Copyright (c) 2018-2019, 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 <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "json.h"
static const char *input_buf;
static int input_pos;
static int input_len;
static int json_parse_array(struct json_value *array);
static int json_parse_object(struct json_value *object);
static int json_parse_property(struct json_value *value);
static int input(void)
{
if (input_pos >= input_len)
return 0;
return input_buf[input_pos++];
}
static void unput(void)
{
input_pos--;
}
static void json_skip_whitespace(void)
{
int ch;
while ((ch = input()) && isspace(ch))
;
unput();
}
static int json_parse_string(struct json_value *value)
{
char buf[128];
char *b = buf;
int ch;
ch = input();
if (ch != '"') {
unput();
return 0;
}
while ((ch = input()) && ch != '"')
*b++ = ch;
*b = '\0';
if (!ch)
return -1;
value->type = JSON_TYPE_STRING;
value->u.string = strdup(buf);
return 1;
}
static int json_parse_number(struct json_value *value)
{
char buf[20];
char *b = buf;
int ch;
while ((ch = input()) && isdigit(ch) && b - buf < sizeof(buf) - 1)
*b++ = ch;
*b = '\0';
unput();
if (b == buf)
return 0;
value->type = JSON_TYPE_NUMBER;
value->u.number = strtod(buf, NULL);
return 1;
}
static int json_parse_keyword(struct json_value *value)
{
const char *match;
const char *m;
int ch;
ch = input();
switch (ch) {
case 't':
match = "true";
value->type = JSON_TYPE_TRUE;
break;
case 'f':
match = "false";
value->type = JSON_TYPE_FALSE;
break;
case 'n':
match = "null";
value->type = JSON_TYPE_NULL;
break;
default:
unput();
return 0;
}
m = match;
while (*m && *m++ == ch)
ch = input();
unput();
return *m == '\0' ? 1 : -1;
}
static int json_parse_value(struct json_value *value)
{
int ret;
json_skip_whitespace();
ret = json_parse_object(value);
if (ret)
goto out;
ret = json_parse_array(value);
if (ret)
goto out;
ret = json_parse_string(value);
if (ret)
goto out;
ret = json_parse_number(value);
if (ret)
goto out;
ret = json_parse_keyword(value);
if (ret)
goto out;
fprintf(stderr, "unable to match a value\n");
return -1;
out:
json_skip_whitespace();
return ret;
}
static int json_parse_array(struct json_value *array)
{
struct json_value *value;
struct json_value *last = NULL;
int ret;
int ch;
ch = input();
if (ch != '[') {
unput();
return 0;
}
array->type = JSON_TYPE_ARRAY;
do {
value = calloc(1, sizeof(*value));
if (!value)
return -1;
ret = json_parse_value(value);
if (ret <= 0) {
free(value);
return -1;
}
if (!array->u.value)
array->u.value = value;
if (last)
last->next = value;
last = value;
ch = input();
if (ch == ']') {
return 1;
}
} while (ch == ',');
fprintf(stderr, "expected ',' got '%c'\n", ch);
return -1;
}
static int json_parse_object(struct json_value *object)
{
struct json_value *value;
struct json_value *last = NULL;
int ret;
int ch;
ch = input();
if (ch != '{') {
unput();
return 0;
}
object->type = JSON_TYPE_OBJECT;
do {
value = calloc(1, sizeof(*value));
if (!value)
return -1;
ret = json_parse_property(value);
if (ret <= 0) {
free(value);
return -1;
}
if (!object->u.value)
object->u.value = value;
if (last)
last->next = value;
last = value;
ch = input();
if (ch == '}') {
return 1;
}
} while (ch == ',');
return -1;
}
static int json_parse_property(struct json_value *value)
{
struct json_value key;
int ret;
int ch;
json_skip_whitespace();
ret = json_parse_string(&key);
if (ret <= 0)
return -1;
value->key = key.u.string;
json_skip_whitespace();
ch = input();
if (ch != ':')
return -1;
ret = json_parse_value(value);
if (ret <= 0)
return -1;
return 1;
}
struct json_value *json_parse(const char *json)
{
struct json_value *root;
int ret;
input_buf = json;
input_pos = 0;
input_len = strlen(input_buf);
root = calloc(1, sizeof(*root));
if (!root)
return NULL;
ret = json_parse_value(root);
if (ret != 1) {
free(root);
return NULL;
}
return root;
}
struct json_value *json_parse_file(const char *file)
{
struct json_value *root;
struct stat sb;
int ret;
int fd;
fd = open(file, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "failed to open %s: %s\n", file, strerror(errno));
return NULL;
}
ret = fstat(fd, &sb);
if (ret < 0)
return NULL;
input_pos = 0;
input_len = sb.st_size;
input_buf = malloc(sb.st_size);
ret = read(fd, (char *)input_buf, input_len);
close(fd);
if (ret != input_len) {
fprintf(stderr, "failed to read %d bytes form %s\n", input_len, file);
return NULL;
}
root = calloc(1, sizeof(*root));
if (!root)
return NULL;
ret = json_parse_value(root);
if (ret != 1) {
free(root);
return NULL;
}
return root;
}
struct json_value *json_get_child(struct json_value *object, const char *key)
{
struct json_value *it;
if(object->type != JSON_TYPE_OBJECT)
return NULL;
for (it = object->u.value; it; it = it->next) {
if (!strcmp(it->key, key))
return it;
}
return NULL;
}
int json_count_children(struct json_value *array)
{
struct json_value *it;
int count = 0;
if (!array || array->type != JSON_TYPE_ARRAY)
return -1;
for (it = array->u.value; it; it = it->next)
count++;
return count;
}
int json_get_number(struct json_value *object, const char *key, double *number)
{
struct json_value *it;
if (!object || object->type != JSON_TYPE_OBJECT)
return -1;
for (it = object->u.value; it; it = it->next) {
if (!strcmp(it->key, key)) {
if (it->type != JSON_TYPE_NUMBER)
return -1;
*number = it->u.number;
return 0;
}
}
return -1;
}
const char *json_get_string(struct json_value *object, const char *key)
{
struct json_value *it;
if (!object || object->type != JSON_TYPE_OBJECT)
return NULL;
for (it = object->u.value; it; it = it->next) {
if (!strcmp(it->key, key)) {
if (it->type != JSON_TYPE_STRING)
return NULL;
return it->u.string;
}
}
return NULL;
}
void json_free(struct json_value *value)
{
struct json_value *next;
struct json_value *it;
free((char *)value->key);
switch (value->type) {
case JSON_TYPE_OBJECT:
case JSON_TYPE_ARRAY:
it = value->u.value;
while (it) {
next = it->next;
json_free(it);
it = next;
}
break;
case JSON_TYPE_STRING:
free((char *)value->u.string);
break;
}
free(value);
}

67
json.h
View File

@@ -1,67 +0,0 @@
/*
* Copyright (c) 2019, 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.
*/
#ifndef __JSON_H__
#define __JSON_H__
enum {
JSON_TYPE_UNKNOWN,
JSON_TYPE_TRUE,
JSON_TYPE_FALSE,
JSON_TYPE_NULL,
JSON_TYPE_NUMBER,
JSON_TYPE_STRING,
JSON_TYPE_ARRAY,
JSON_TYPE_OBJECT,
};
struct json_value {
const char *key;
int type;
union {
double number;
const char *string;
struct json_value *value;
} u;
struct json_value *next;
};
struct json_value *json_parse(const char *json);
struct json_value *json_parse_file(const char *file);
int json_count_children(struct json_value *array);
struct json_value *json_get_child(struct json_value *object, const char *key);
int json_get_number(struct json_value *object, const char *key, double *number);
const char *json_get_string(struct json_value *object, const char *key);
void json_free(struct json_value *value);
#endif

131
ks.c Normal file
View File

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

View File

@@ -1,146 +0,0 @@
#define NBDKIT_API_VERSION 2
#include <nbdkit-plugin.h>
#define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS
#include <linux/usbdevice_fs.h>
#include <linux/usb/ch9.h>
#include <sys/ioctl.h>
#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 <libudev.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 <libxml/parser.h>
#include <libxml/tree.h>
#include "qdl.h"
#include "patch.h"
#include "ufs.h"
#include "usb.h"
static const char *config_programmer;
static bool config_ufs = true;
static int config_lun = 1;
static size_t sector_size;
bool qdl_debug;
static int qdl_config(const char *key, const char *value)
{
if (!strcmp(key, "programmer")) {
config_programmer = nbdkit_absolute_path(value);
} else if (!strcmp(key, "storage")) {
if (!strcmp(value, "ufs")) {
config_ufs = true;
} else if (!strcmp(value, "emmc")) {
config_ufs = false;
} else {
nbdkit_error("unknown storage type '%s'", value);
return -1;
}
} else if (!strcmp(key, "lun")) {
config_lun = atoi(value);
} else if (!strcmp(key, "debug")) {
qdl_debug = true;
} else {
return -1;
}
return 0;
}
static int qdl_config_complete(void)
{
if (!config_programmer)
return -1;
return 0;
}
static void *qdl_open(int readonly)
{
struct qdl_device *qdl;
int ret;
qdl = usb_open();
if (!qdl)
return NULL;
ret = sahara_run(qdl, config_programmer);
if (ret < 0)
return NULL;
ret = firehose_open(qdl, config_ufs);
if (ret < 0)
return NULL;
return qdl;
}
static void qdl_close(void *handle)
{
struct qdl_device *qdl = handle;
firehose_reset(qdl);
free(qdl);
}
static int64_t qdl_get_size(void *handle)
{
struct qdl_device *qdl = handle;
size_t num_sectors;
int ret;
ret = firehose_getsize(qdl, config_lun, &sector_size, &num_sectors);
if (ret < 0)
return -1;
return sector_size * num_sectors;
}
static int qdl_pread(void *handle, void *buf, uint32_t count, uint64_t offset,
uint32_t flags)
{
struct qdl_device *qdl = handle;
return firehose_pread(qdl, config_lun, offset / sector_size, buf,
sector_size, count / sector_size);
}
static int qdl_pwrite(void *handle, const void *buf, uint32_t count,
uint64_t offset, uint32_t flags)
{
struct qdl_device *qdl = handle;
return firehose_pwrite(qdl, config_lun, offset / sector_size, buf,
sector_size, count / sector_size);
}
static struct nbdkit_plugin qdl_plugin = {
.name = "qdl",
.description = "nbdkit Qualcomm Download plugin",
.open = qdl_open,
.close = qdl_close,
.config = qdl_config,
.config_complete = qdl_config_complete,
.get_size = qdl_get_size,
.pread = qdl_pread,
.pwrite = qdl_pwrite,
};
NBDKIT_REGISTER_PLUGIN(qdl_plugin)

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

69
patch.c
View File

@@ -1,41 +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;
@@ -49,7 +26,7 @@ int patch_load(const char *patch_file)
doc = xmlReadFile(patch_file, NULL, 0);
if (!doc) {
fprintf(stderr, "[PATCH] failed to parse %s\n", patch_file);
ux_err("failed to parse patch-type file \"%s\"\n", patch_file);
return -EINVAL;
}
@@ -58,8 +35,8 @@ int patch_load(const char *patch_file)
if (node->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp(node->name, (xmlChar*)"patch")) {
fprintf(stderr, "[PATCH] unrecognized tag \"%s\", ignoring\n", node->name);
if (xmlStrcmp(node->name, (xmlChar *)"patch")) {
ux_err("unrecognized tag \"%s\" in patch-type file, ignoring\n", node->name);
continue;
}
@@ -77,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;
}
@@ -95,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;
@@ -108,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

409
program.c
View File

@@ -1,57 +1,234 @@
// 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>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "program.h"
#include "qdl.h"
#include "oscompat.h"
#include "sparse.h"
static struct program *programes;
static struct program *programes_last;
int program_load(const char *program_file)
static int load_erase_tag(xmlNode *node, bool is_nand)
{
struct program *program;
int errors = 0;
program = calloc(1, sizeof(struct program));
program->is_nand = is_nand;
program->is_erase = true;
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) {
ux_err("errors while parsing erase tag\n");
free(program);
return -EINVAL;
}
if (programes) {
programes_last->next = program;
programes_last = program;
} else {
programes = program;
programes_last = program;
}
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;
int errors = 0;
program = calloc(1, sizeof(struct program));
program->is_nand = is_nand;
program->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
program->filename = attr_as_string(node, "filename", &errors);
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 (xmlGetProp(node, (xmlChar *)"last_sector")) {
program->last_sector = attr_as_unsigned(node, "last_sector", &errors);
}
} else {
program->file_offset = attr_as_unsigned(node, "file_sector_offset", &errors);
}
if (errors) {
ux_err("errors while parsing program tag\n");
free(program);
return -EINVAL;
}
if (programes) {
programes_last->next = program;
programes_last = program;
} else {
programes = program;
programes_last = program;
}
return 0;
}
int program_load(const char *program_file, bool is_nand)
{
xmlNode *node;
xmlNode *root;
xmlDoc *doc;
int errors;
int errors = 0;
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;
}
@@ -60,54 +237,37 @@ int program_load(const char *program_file)
if (node->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp(node->name, (xmlChar*)"program")) {
fprintf(stderr, "[PROGRAM] unrecognized tag \"%s\", ignoring\n", node->name);
continue;
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);
else {
ux_err("unrecognized tag \"%s\" in program-type file \"%s\"\n", node->name, program_file);
errors = -EINVAL;
}
errors = 0;
program = calloc(1, sizeof(struct program));
program->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
program->file_offset = attr_as_unsigned(node, "file_sector_offset", &errors);
program->filename = attr_as_string(node, "filename", &errors);
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->start_sector = attr_as_string(node, "start_sector", &errors);
if (errors) {
fprintf(stderr, "[PROGRAM] errors while parsing program\n");
free(program);
continue;
}
if (programes) {
programes_last->next = program;
programes_last = program;
} else {
programes = program;
programes_last = program;
}
if (errors)
goto out;
}
out:
xmlFreeDoc(doc);
return 0;
return errors;
}
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
const char *incdir)
const char *incdir, bool allow_missing)
{
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) {
if (!program->filename)
if (program->is_erase || !program->filename)
continue;
filename = program->filename;
@@ -117,14 +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...ignoring\n", program->filename);
ux_info("unable to open %s", program->filename);
if (!allow_missing) {
ux_info("...failing\n");
return -1;
}
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)
@@ -134,6 +313,40 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
return 0;
}
int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program))
{
struct program *program;
int ret;
for (program = programes; program; program = program->next) {
if (!program->is_erase)
continue;
ret = apply(qdl, program);
if (ret)
return ret;
}
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
*
@@ -141,25 +354,71 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
*
* 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;
*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;
}

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