55 Commits
nbdkit ... v2.1

Author SHA1 Message Date
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
24 changed files with 1400 additions and 1348 deletions

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

@@ -0,0 +1,32 @@
name: Buildtest
on:
pull_request:
push:
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, macos-latest ]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Dependencies (Ubuntu)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libxml2-dev libusb-1.0-0-dev
- name: Dependencies (macOS)
if: runner.os == 'macOS'
run: |
brew install libxml2
brew install libusb
- name: Build
run: make

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

4
.gitignore vendored
View File

@@ -1,2 +1,6 @@
*.o
qdl
qdl-ramdump
ks
compile_commands.json
.cache

View File

@@ -1,33 +1,40 @@
OUT := qdl
NBDKIT := nbdkit-qdl-plugin.so
QDL := qdl
RAMDUMP := qdl-ramdump
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`
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 qdl.c sahara.c util.c patch.c program.c read.c ufs.c usb.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 usb.c util.c
RAMDUMP_OBJS := $(RAMDUMP_SRCS:.c=.o)
$(OUT): $(QDL_OBJS)
KS_OUT := ks
KS_SRCS := ks.c sahara.c util.c
KS_OBJS := $(KS_SRCS:.c=.o)
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:.}]" > $@
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
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

33
README
View File

@@ -7,37 +7,10 @@ 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.
In order to build the project you need libxml2 and libusb-1.0 headers
and libraries, found in e.g. the libxml2-dev and libusb-1.0.0-dev packages
With this installed run:
With these installed run:
make
or to build the nbdkit plugin run:
make lib

File diff suppressed because it is too large Load Diff

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

117
ks.c Normal file
View File

@@ -0,0 +1,117 @@
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <poll.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "qdl.h"
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[] = {
{"port", required_argument, 0, 'p'},
{"sahara", required_argument, 0, 's'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "p:s:", options, NULL )) != -1) {
switch (opt) {
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;
}
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)

View File

@@ -30,6 +30,7 @@
*/
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

159
program.c
View File

@@ -31,23 +31,103 @@
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "program.h"
#include "qdl.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;
if (!is_nand) {
fprintf(stderr, "got \"erase\" tag for non-NAND storage\n");
return -EINVAL;
}
program = calloc(1, sizeof(struct program));
program->is_nand = true;
program->is_erase = true;
program->pages_per_block = attr_as_unsigned(node, "PAGES_PER_BLOCK", &errors);
program->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
program->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
program->start_sector = attr_as_string(node, "start_sector", &errors);
if (errors) {
fprintf(stderr, "[PROGRAM] 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 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->start_sector = attr_as_string(node, "start_sector", &errors);
if (is_nand) {
program->pages_per_block = attr_as_unsigned(node, "PAGES_PER_BLOCK", &errors);
if (NULL != xmlGetProp(node, (xmlChar *)"last_sector")) {
program->last_sector = attr_as_unsigned(node, "last_sector", &errors);
}
} else {
program->file_offset = attr_as_unsigned(node, "file_sector_offset", &errors);
}
if (errors) {
fprintf(stderr, "[PROGRAM] errors while parsing program\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) {
@@ -60,45 +140,27 @@ 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 {
fprintf(stderr, "[PROGRAM] unrecognized tag \"%s\"\n", node->name);
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;
const char *filename;
@@ -107,7 +169,7 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
int fd;
for (program = programes; program; program = program->next) {
if (!program->filename)
if (program->is_erase || !program->filename)
continue;
filename = program->filename;
@@ -120,7 +182,12 @@ int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl,
fd = open(filename, O_RDONLY);
if (fd < 0) {
printf("Unable to open %s...ignoring\n", program->filename);
printf("Unable to open %s", program->filename);
if (!allow_missing) {
printf("...failing\n");
return -1;
}
printf("...ignoring\n");
continue;
}
@@ -134,6 +201,24 @@ 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;
}
/**
* program_find_bootable_partition() - find one bootable partition
*
@@ -151,6 +236,8 @@ int program_find_bootable_partition(void)
for (program = programes; program; program = program->next) {
label = program->label;
if (!label)
continue;
if (!strcmp(label, "xbl") || !strcmp(label, "xbl_a") ||
!strcmp(label, "sbl1")) {

View File

@@ -5,6 +5,7 @@
#include "qdl.h"
struct program {
unsigned pages_per_block;
unsigned sector_size;
unsigned file_offset;
const char *filename;
@@ -12,13 +13,18 @@ struct program {
unsigned num_sectors;
unsigned partition;
const char *start_sector;
unsigned last_sector;
bool is_nand;
bool is_erase;
struct program *next;
};
int program_load(const char *program_file);
int program_load(const char *program_file, bool is_nand);
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
const char *incdir);
const char *incdir, bool allow_missing);
int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program));
int program_find_bootable_partition(void);
#endif

79
qdl.c
View File

@@ -29,43 +29,33 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#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"
#define MAX_USBFS_BULK_SIZE (16*1024)
enum {
QDL_FILE_UNKNOWN,
QDL_FILE_PATCH,
QDL_FILE_PROGRAM,
QDL_FILE_READ,
QDL_FILE_UFS,
QDL_FILE_CONTENTS,
};
bool qdl_debug;
static struct qdl_device qdl;
static int detect_type(const char *xml_file)
{
@@ -91,6 +81,10 @@ static int detect_type(const char *xml_file)
type = QDL_FILE_PROGRAM;
break;
}
if (!xmlStrcmp(node->name, (xmlChar*)"read")) {
type = QDL_FILE_READ;
break;
}
if (!xmlStrcmp(node->name, (xmlChar*)"ufs")) {
type = QDL_FILE_UFS;
break;
@@ -109,44 +103,61 @@ static void print_usage(void)
{
extern const char *__progname;
fprintf(stderr,
"%s [--debug] [--storage <emmc|ufs>] [--finalize-provisioning] [--include <PATH>] <prog.mbn> [<program> <patch> ...]\n",
"%s [--debug] [--allow-missing] [--storage <emmc|nand|ufs>] [--finalize-provisioning] [--include <PATH>] [--serial <NUM>] [--out-chunk-size <SIZE>] <prog.mbn> [<program> <patch> ...]\n",
__progname);
}
enum {
OPT_OUT_CHUNK_SIZE = 1000,
};
int main(int argc, char **argv)
{
struct qdl_device *qdl;
char *prog_mbn, *storage="ufs";
char *incdir = NULL;
int bootable;
char *serial = NULL;
int type;
int ret;
int opt;
bool qdl_finalize_provisioning = false;
bool allow_missing = false;
long out_chunk_size;
static struct option options[] = {
{"debug", no_argument, 0, 'd'},
{"include", required_argument, 0, 'i'},
{"finalize-provisioning", no_argument, 0, 'l'},
{"out-chunk-size", required_argument, 0, OPT_OUT_CHUNK_SIZE },
{"serial", required_argument, 0, 'S'},
{"storage", required_argument, 0, 's'},
{"allow-missing", no_argument, 0, 'f'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "di:", options, NULL )) != -1) {
while ((opt = getopt_long(argc, argv, "dfi:S:", options, NULL )) != -1) {
switch (opt) {
case 'd':
qdl_debug = true;
break;
case 'f':
allow_missing = true;
break;
case 'i':
incdir = optarg;
break;
case 'l':
qdl_finalize_provisioning = true;
break;
case OPT_OUT_CHUNK_SIZE:
out_chunk_size = strtol(optarg, NULL, 10);
qdl_set_out_chunk_size(&qdl, out_chunk_size);
break;
case 's':
storage = optarg;
break;
case 'S':
serial = optarg;
break;
default:
print_usage();
return 1;
@@ -173,10 +184,15 @@ int main(int argc, char **argv)
errx(1, "patch_load %s failed", argv[optind]);
break;
case QDL_FILE_PROGRAM:
ret = program_load(argv[optind]);
ret = program_load(argv[optind], !strcmp(storage, "nand"));
if (ret < 0)
errx(1, "program_load %s failed", argv[optind]);
break;
case QDL_FILE_READ:
ret = read_op_load(argv[optind]);
if (ret < 0)
errx(1, "read_op_load %s failed", argv[optind]);
break;
case QDL_FILE_UFS:
ret = ufs_load(argv[optind],qdl_finalize_provisioning);
if (ret < 0)
@@ -188,33 +204,18 @@ int main(int argc, char **argv)
}
} while (++optind < argc);
qdl = usb_open();
ret = qdl_open(&qdl, serial);
if (ret)
return 1;
ret = sahara_run(qdl, prog_mbn);
qdl.mappings[0] = prog_mbn;
ret = sahara_run(&qdl, qdl.mappings, true, NULL, NULL);
if (ret < 0)
return 1;
ret = firehose_open(qdl, !strcmp(storage, "ufs"));
ret = firehose_run(&qdl, incdir, storage, allow_missing);
if (ret < 0)
return 1;
ret = program_execute(qdl, firehose_program, incdir);
if (ret)
return 1;
ret = patch_execute(qdl, firehose_apply_patch);
if (ret)
return 1;
bootable = program_find_bootable_partition();
if (bootable < 0)
fprintf(stderr, "no boot partition found\n");
else
firehose_set_bootable(qdl, bootable);
firehose_reset(qdl);
return 0;
}

43
qdl.h
View File

@@ -5,30 +5,39 @@
#include "patch.h"
#include "program.h"
#include "read.h"
#include <libxml/tree.h>
struct qdl_device;
struct program;
struct patch;
#define MAPPING_SZ 64
int firehose_open(struct qdl_device *qdl, bool ufs);
int sahara_run(struct qdl_device *qdl, const char *prog_mbn);
struct libusb_device_handle;
struct qdl_device {
struct libusb_device_handle *usb_handle;
int fd;
int in_ep;
int out_ep;
size_t in_maxpktsize;
size_t out_maxpktsize;
size_t out_chunk_size;
char *mappings[MAPPING_SZ]; // array index is the id from the device
};
int qdl_open(struct qdl_device *qdl, const char *serial);
int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout);
int qdl_write(struct qdl_device *qdl, const void *buf, size_t len);
void qdl_set_out_chunk_size(struct qdl_device *qdl, long size);
int firehose_run(struct qdl_device *qdl, const char *incdir, const char *storage, bool allow_missing);
int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
const char *ramdump_path, const char *ramdump_filter);
void print_hex_dump(const char *prefix, const void *buf, size_t len);
unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors);
const char *attr_as_string(xmlNode *node, const char *attr, int *errors);
int firehose_getsize(struct qdl_device *qdl, int lun, size_t *sector_size,
size_t *num_sectors);
int firehose_reset(struct qdl_device *qdl);
int firehose_set_bootable(struct qdl_device *qdl, int part);
int firehose_program(struct qdl_device *qdl, struct program *program, int fd);
int firehose_apply_patch(struct qdl_device *qdl, struct patch *patch);
ssize_t firehose_pread(struct qdl_device *qdl, int lun, size_t offset, void *buf,
size_t sector_size, size_t num_sectors);
ssize_t firehose_pwrite(struct qdl_device *qdl, int lun, size_t offset,
const void *buf, size_t sector_size, size_t num_sectors);
extern bool qdl_debug;
#endif

66
ramdump.c Normal file
View File

@@ -0,0 +1,66 @@
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "qdl.h"
bool qdl_debug;
static void print_usage(void)
{
extern const char *__progname;
fprintf(stderr,
"%s [--debug] [-o <ramdump-path>] [segment-filter,...]\n",
__progname);
exit(1);
}
int main(int argc, char **argv)
{
struct qdl_device qdl;
char *ramdump_path = ".";
char *filter = NULL;
char *serial = NULL;
int ret;
int opt;
static struct option options[] = {
{"debug", no_argument, 0, 'd'},
{"output", required_argument, 0, 'o'},
{"serial", required_argument, 0, 'S'},
{0, 0, 0, 0}
};
while ((opt = getopt_long(argc, argv, "do:S:", options, NULL )) != -1) {
switch (opt) {
case 'd':
qdl_debug = true;
break;
case 'o':
ramdump_path = optarg;
break;
case 'S':
serial = optarg;
break;
default:
print_usage();
}
}
if (optind < argc)
filter = argv[optind++];
if (optind != argc)
print_usage();
ret = qdl_open(&qdl, serial);
if (ret)
return 1;
ret = sahara_run(&qdl, NULL, true, ramdump_path, filter);
if (ret < 0)
return 1;
return 0;
}

131
read.c Normal file
View File

@@ -0,0 +1,131 @@
/*
* 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 <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "read.h"
#include "qdl.h"
static struct read_op *read_ops;
static struct read_op *read_ops_last;
int read_op_load(const char *read_op_file)
{
struct read_op *read_op;
xmlNode *node;
xmlNode *root;
xmlDoc *doc;
int errors;
doc = xmlReadFile(read_op_file, NULL, 0);
if (!doc) {
fprintf(stderr, "[READ] failed to parse %s\n", read_op_file);
return -EINVAL;
}
root = xmlDocGetRootElement(doc);
for (node = root->children; node ; node = node->next) {
if (node->type != XML_ELEMENT_NODE)
continue;
if (xmlStrcmp(node->name, (xmlChar*)"read")) {
fprintf(stderr, "[READ] unrecognized tag \"%s\", ignoring\n", node->name);
continue;
}
errors = 0;
read_op = calloc(1, sizeof(struct read_op));
read_op->sector_size = attr_as_unsigned(node, "SECTOR_SIZE_IN_BYTES", &errors);
read_op->filename = attr_as_string(node, "filename", &errors);
read_op->partition = attr_as_unsigned(node, "physical_partition_number", &errors);
read_op->num_sectors = attr_as_unsigned(node, "num_partition_sectors", &errors);
read_op->start_sector = attr_as_string(node, "start_sector", &errors);
if (errors) {
fprintf(stderr, "[READ] errors while parsing read\n");
free(read_op);
continue;
}
if (read_ops) {
read_ops_last->next = read_op;
read_ops_last = read_op;
} else {
read_ops = read_op;
read_ops_last = read_op;
}
}
xmlFreeDoc(doc);
return 0;
}
int read_op_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd),
const char *incdir)
{
struct read_op *read_op;
const char *filename;
char tmp[PATH_MAX];
int ret;
int fd;
for (read_op = read_ops; read_op; read_op = read_op->next) {
filename = read_op->filename;
if (incdir) {
snprintf(tmp, PATH_MAX, "%s/%s", incdir, filename);
if (access(tmp, F_OK) != -1)
filename = tmp;
}
fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (fd < 0) {
printf("Unable to open %s...\n", read_op->filename);
return ret;
}
ret = apply(qdl, read_op, fd);
close(fd);
if (ret)
return ret;
}
return 0;
}

22
read.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef __READ_H__
#define __READ_H__
#include <stdbool.h>
struct qdl_device;
struct read_op {
unsigned sector_size;
const char *filename;
unsigned partition;
unsigned num_sectors;
const char *start_sector;
struct read_op *next;
};
int read_op_load(const char *read_op_file);
int read_op_execute(struct qdl_device *qdl,
int (*apply)(struct qdl_device *qdl, struct read_op *read_op, int fd),
const char *incdir);
#endif

330
sahara.c
View File

@@ -35,6 +35,7 @@
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <inttypes.h>
#include <poll.h>
#include <stdbool.h>
@@ -45,7 +46,49 @@
#include <termios.h>
#include <unistd.h>
#include "qdl.h"
#include "usb.h"
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define SAHARA_HELLO_CMD 0x1 /* Min protocol version 1.0 */
#define SAHARA_HELLO_RESP_CMD 0x2 /* Min protocol version 1.0 */
#define SAHARA_READ_DATA_CMD 0x3 /* Min protocol version 1.0 */
#define SAHARA_END_OF_IMAGE_CMD 0x4 /* Min protocol version 1.0 */
#define SAHARA_DONE_CMD 0x5 /* Min protocol version 1.0 */
#define SAHARA_DONE_RESP_CMD 0x6 /* Min protocol version 1.0 */
#define SAHARA_RESET_CMD 0x7 /* Min protocol version 1.0 */
#define SAHARA_RESET_RESP_CMD 0x8 /* Min protocol version 1.0 */
#define SAHARA_MEM_DEBUG_CMD 0x9 /* Min protocol version 2.0 */
#define SAHARA_MEM_READ_CMD 0xa /* Min protocol version 2.0 */
#define SAHARA_CMD_READY_CMD 0xb /* Min protocol version 2.1 */
#define SAHARA_SWITCH_MODE_CMD 0xc /* Min protocol version 2.1 */
#define SAHARA_EXECUTE_CMD 0xd /* Min protocol version 2.1 */
#define SAHARA_EXECUTE_RESP_CMD 0xe /* Min protocol version 2.1 */
#define SAHARA_EXECUTE_DATA_CMD 0xf /* Min protocol version 2.1 */
#define SAHARA_MEM_DEBUG64_CMD 0x10 /* Min protocol version 2.5 */
#define SAHARA_MEM_READ64_CMD 0x11 /* Min protocol version 2.5 */
#define SAHARA_READ_DATA64_CMD 0x12 /* Min protocol version 2.8 */
#define SAHARA_RESET_STATE_CMD 0x13 /* Min protocol version 2.9 */
#define SAHARA_WRITE_DATA_CMD 0x14 /* Min protocol version 3.0 */
#define SAHARA_VERSION 2
#define SAHARA_SUCCESS 0
#define SAHARA_MODE_IMAGE_TX_PENDING 0x0
#define SAHARA_MODE_IMAGE_TX_COMPLETE 0x1
#define SAHARA_MODE_MEMORY_DEBUG 0x2
#define SAHARA_MODE_COMMAND 0x3
#define SAHARA_HELLO_LENGTH 0x30
#define SAHARA_READ_DATA_LENGTH 0x14
#define SAHARA_READ_DATA64_LENGTH 0x20
#define SAHARA_END_OF_IMAGE_LENGTH 0x10
#define SAHARA_MEM_READ64_LENGTH 0x18
#define SAHARA_MEM_DEBUG64_LENGTH 0x18
#define SAHARA_DONE_LENGTH 0x8
#define SAHARA_DONE_RESP_LENGTH 0xc
#define SAHARA_RESET_LENGTH 0x8
#define DEBUG_BLOCK_SIZE (512*1024)
struct sahara_pkt {
uint32_t cmd;
@@ -78,6 +121,10 @@ struct sahara_pkt {
struct {
uint32_t status;
} done_resp;
struct {
uint64_t addr;
uint64_t length;
} debug64_req;
struct {
uint64_t image;
uint64_t offset;
@@ -86,36 +133,49 @@ struct sahara_pkt {
};
};
struct sahara_debug_region64 {
uint64_t type;
uint64_t addr;
uint64_t length;
char region[20];
char filename[20];
};
static void sahara_send_reset(struct qdl_device *qdl)
{
struct sahara_pkt resp;
resp.cmd = SAHARA_RESET_CMD;
resp.length = SAHARA_RESET_LENGTH;
qdl_write(qdl, &resp, resp.length);
}
static void sahara_hello(struct qdl_device *qdl, struct sahara_pkt *pkt)
{
struct sahara_pkt resp;
assert(pkt->length == 0x30);
assert(pkt->length == SAHARA_HELLO_LENGTH);
printf("HELLO version: 0x%x compatible: 0x%x max_len: %d mode: %d\n",
pkt->hello_req.version, pkt->hello_req.compatible, pkt->hello_req.max_len, pkt->hello_req.mode);
resp.cmd = 2;
resp.length = 0x30;
resp.hello_resp.version = 2;
resp.cmd = SAHARA_HELLO_RESP_CMD;
resp.length = SAHARA_HELLO_LENGTH;
resp.hello_resp.version = SAHARA_VERSION;
resp.hello_resp.compatible = 1;
resp.hello_resp.status = 0;
resp.hello_resp.status = SAHARA_SUCCESS;
resp.hello_resp.mode = pkt->hello_req.mode;
qdl_write(qdl, &resp, resp.length, true);
qdl_write(qdl, &resp, resp.length);
}
static int sahara_read_common(struct qdl_device *qdl, const char *mbn, off_t offset, size_t len)
static int sahara_read_common(struct qdl_device *qdl, int progfd, off_t offset, size_t len)
{
int progfd;
ssize_t n;
void *buf;
int ret = 0;
progfd = open(mbn, O_RDONLY);
if (progfd < 0)
return -errno;
buf = malloc(len);
if (!buf)
return -ENOMEM;
@@ -127,50 +187,96 @@ static int sahara_read_common(struct qdl_device *qdl, const char *mbn, off_t off
goto out;
}
n = qdl_write(qdl, buf, n, true);
n = qdl_write(qdl, buf, n);
if (n != len)
err(1, "failed to write %zu bytes to sahara", len);
free(buf);
close(progfd);
out:
return ret;
}
static void sahara_read(struct qdl_device *qdl, struct sahara_pkt *pkt, const char *mbn)
static void sahara_read(struct qdl_device *qdl, struct sahara_pkt *pkt, char *img_arr[], bool single_image)
{
unsigned int image;
int ret;
int fd;
assert(pkt->length == 0x14);
assert(pkt->length == SAHARA_READ_DATA_LENGTH);
printf("READ image: %d offset: 0x%x length: 0x%x\n",
pkt->read_req.image, pkt->read_req.offset, pkt->read_req.length);
ret = sahara_read_common(qdl, mbn, pkt->read_req.offset, pkt->read_req.length);
if (single_image)
image = 0;
else
image = pkt->read_req.image;
if (image >= MAPPING_SZ || !img_arr[image]) {
fprintf(stderr, "Device specified invalid image: %u\n", image);
sahara_send_reset(qdl);
return;
}
fd = open(img_arr[image], O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Can not open %s: %s\n", img_arr[image], strerror(errno));
// Maybe this read was optional. Notify device of error and let
// it decide how to proceed.
sahara_send_reset(qdl);
return;
}
ret = sahara_read_common(qdl, fd, pkt->read_req.offset, pkt->read_req.length);
if (ret < 0)
errx(1, "failed to read image chunk to sahara");
close(fd);
}
static void sahara_read64(struct qdl_device *qdl, struct sahara_pkt *pkt, const char *mbn)
static void sahara_read64(struct qdl_device *qdl, struct sahara_pkt *pkt, char *img_arr[], bool single_image)
{
unsigned int image;
int ret;
int fd;
assert(pkt->length == 0x20);
assert(pkt->length == SAHARA_READ_DATA64_LENGTH);
printf("READ64 image: %" PRId64 " offset: 0x%" PRIx64 " length: 0x%" PRIx64 "\n",
pkt->read64_req.image, pkt->read64_req.offset, pkt->read64_req.length);
ret = sahara_read_common(qdl, mbn, pkt->read64_req.offset, pkt->read64_req.length);
if (single_image)
image = 0;
else
image = pkt->read64_req.image;
if (image >= MAPPING_SZ || !img_arr[image]) {
fprintf(stderr, "Device specified invalid image: %u\n", image);
sahara_send_reset(qdl);
return;
}
fd = open(img_arr[image], O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Can not open %s: %s\n", img_arr[image], strerror(errno));
// Maybe this read was optional. Notify device of error and let
// it decide how to proceed.
sahara_send_reset(qdl);
return;
}
ret = sahara_read_common(qdl, fd, pkt->read64_req.offset, pkt->read64_req.length);
if (ret < 0)
errx(1, "failed to read image chunk to sahara");
close(fd);
}
static void sahara_eoi(struct qdl_device *qdl, struct sahara_pkt *pkt)
{
struct sahara_pkt done;
assert(pkt->length == 0x10);
assert(pkt->length == SAHARA_END_OF_IMAGE_LENGTH);
printf("END OF IMAGE image: %d status: %d\n", pkt->eoi.image, pkt->eoi.status);
@@ -179,28 +285,165 @@ static void sahara_eoi(struct qdl_device *qdl, struct sahara_pkt *pkt)
return;
}
done.cmd = 5;
done.length = 0x8;
qdl_write(qdl, &done, done.length, true);
done.cmd = SAHARA_DONE_CMD;
done.length = SAHARA_DONE_LENGTH;
qdl_write(qdl, &done, done.length);
}
static int sahara_done(struct qdl_device *qdl, struct sahara_pkt *pkt)
{
assert(pkt->length == 0xc);
assert(pkt->length == SAHARA_DONE_RESP_LENGTH);
printf("DONE status: %d\n", pkt->done_resp.status);
// 0 == PENDING, 1 == COMPLETE. Device expects more images if
// PENDING is set in status.
return pkt->done_resp.status;
}
int sahara_run(struct qdl_device *qdl, const char *prog_mbn)
static ssize_t sahara_debug64_one(struct qdl_device *qdl,
struct sahara_debug_region64 region,
int ramdump_dir)
{
struct sahara_pkt read_req;
uint64_t remain;
size_t offset;
size_t chunk;
ssize_t n;
void *buf;
int fd;
buf = malloc(DEBUG_BLOCK_SIZE);
if (!buf)
return -1;
fd = openat(ramdump_dir, region.filename, O_WRONLY | O_CREAT, 0644);
if (fd < 0) {
warn("failed to open \"%s\"", region.filename);
return -1;
}
chunk = 0;
while (chunk < region.length) {
remain = MIN(region.length - chunk, DEBUG_BLOCK_SIZE);
read_req.cmd = SAHARA_MEM_READ64_CMD;
read_req.length = SAHARA_MEM_READ64_LENGTH;
read_req.debug64_req.addr = region.addr + chunk;
read_req.debug64_req.length = remain;
n = qdl_write(qdl, &read_req, read_req.length);
if (n < 0)
break;
offset = 0;
while (offset < remain) {
n = qdl_read(qdl, buf, DEBUG_BLOCK_SIZE, 30000);
if (n < 0) {
warn("failed to read ramdump chunk");
goto out;
}
write(fd, buf, n);
offset += n;
}
qdl_read(qdl, buf, DEBUG_BLOCK_SIZE, 10);
chunk += DEBUG_BLOCK_SIZE;
}
out:
close(fd);
free(buf);
return 0;
}
static bool sahara_debug64_filter(const char *filename, const char *filter)
{
bool anymatch = false;
char *ptr;
char *tmp;
char *s;
if (!filter)
return false;
tmp = strdup(filter);
for (s = strtok_r(tmp, ",", &ptr); s; s = strtok_r(NULL, ",", &ptr)) {
if (fnmatch(s, filename, 0) == 0) {
anymatch = true;
break;
}
}
free(tmp);
return !anymatch;
}
static void sahara_debug64(struct qdl_device *qdl, struct sahara_pkt *pkt,
int ramdump_dir, const char *filter)
{
struct sahara_debug_region64 *table;
struct sahara_pkt read_req;
ssize_t n;
int i;
assert(pkt->length == SAHARA_MEM_DEBUG64_LENGTH);
printf("DEBUG64 address: 0x%" PRIx64 " length: 0x%" PRIx64 "\n",
pkt->debug64_req.addr, pkt->debug64_req.length);
read_req.cmd = SAHARA_MEM_READ64_CMD;
read_req.length = SAHARA_MEM_READ64_LENGTH;
read_req.debug64_req.addr = pkt->debug64_req.addr;
read_req.debug64_req.length = pkt->debug64_req.length;
n = qdl_write(qdl, &read_req, read_req.length);
if (n < 0)
return;
table = malloc(read_req.debug64_req.length);
n = qdl_read(qdl, table, pkt->debug64_req.length, 1000);
if (n < 0)
return;
for (i = 0; i < pkt->debug64_req.length / sizeof(table[0]); i++) {
if (sahara_debug64_filter(table[i].filename, filter))
continue;
printf("%-2d: type 0x%" PRIx64 " address: 0x%" PRIx64 " length: 0x%" PRIx64 " region: %s filename: %s\n",
i, table[i].type, table[i].addr, table[i].length, table[i].region, table[i].filename);
n = sahara_debug64_one(qdl, table[i], ramdump_dir);
if (n < 0)
break;
}
free(table);
sahara_send_reset(qdl);
}
int sahara_run(struct qdl_device *qdl, char *img_arr[], bool single_image,
const char *ramdump_path, const char *ramdump_filter)
{
struct sahara_pkt *pkt;
int ramdump_dir = -1;
char buf[4096];
char tmp[32];
bool done = false;
int n;
if (ramdump_path) {
ramdump_dir = open(ramdump_path, O_DIRECTORY);
if (ramdump_dir < 0)
err(1, "failed to open directory for ramdump output");
}
while (!done) {
n = qdl_read(qdl, buf, sizeof(buf), 1000);
if (n < 0)
@@ -208,26 +451,37 @@ int sahara_run(struct qdl_device *qdl, const char *prog_mbn)
pkt = (struct sahara_pkt*)buf;
if (n != pkt->length) {
fprintf(stderr, "length not matching");
fprintf(stderr, "length not matching\n");
return -EINVAL;
}
switch (pkt->cmd) {
case 1:
case SAHARA_HELLO_CMD:
sahara_hello(qdl, pkt);
break;
case 3:
sahara_read(qdl, pkt, prog_mbn);
case SAHARA_READ_DATA_CMD:
sahara_read(qdl, pkt, img_arr, single_image);
break;
case 4:
case SAHARA_END_OF_IMAGE_CMD:
sahara_eoi(qdl, pkt);
break;
case 6:
sahara_done(qdl, pkt);
done = true;
case SAHARA_DONE_RESP_CMD:
done = sahara_done(qdl, pkt);
/* E.g MSM8916 EDL reports done = 0 here */
if (single_image)
done = true;
break;
case 0x12:
sahara_read64(qdl, pkt, prog_mbn);
case SAHARA_MEM_DEBUG64_CMD:
sahara_debug64(qdl, pkt, ramdump_dir, ramdump_filter);
break;
case SAHARA_READ_DATA64_CMD:
sahara_read64(qdl, pkt, img_arr, single_image);
break;
case SAHARA_RESET_RESP_CMD:
assert(pkt->length == SAHARA_RESET_LENGTH);
if (ramdump_path)
done = true;
break;
default:
sprintf(tmp, "CMD%x", pkt->cmd);
@@ -236,5 +490,7 @@ int sahara_run(struct qdl_device *qdl, const char *prog_mbn)
}
}
close(ramdump_dir);
return done ? 0 : -1;
}

7
ufs.c
View File

@@ -86,6 +86,13 @@ struct ufs_common *ufs_parse_common_params(xmlNode *node, bool finalize_provisio
return NULL;
}
/* These parameters are optional */
errors = 0;
result->bWriteBoosterBufferPreserveUserSpaceEn = !!attr_as_unsigned(node, "bWriteBoosterBufferPreserveUserSpaceEn", &errors);
result->bWriteBoosterBufferType = !!attr_as_unsigned(node, "bWriteBoosterBufferType", &errors);
result->shared_wb_buffer_size_in_kb = attr_as_unsigned(node, "shared_wb_buffer_size_in_kb", &errors);
result->wb = !errors;
return result;
}

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