89 Commits
v1.0 ... 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
John Stultz
760b3dffb0 qdl: Rework qdl_write to limit write sizes to out_maxpktsize
On a number of machines, qdl could cause crashes on the host
system it ran on, due to swiotlb exaustion.

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

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

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

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

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

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

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

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

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

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

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

Fixes: 5ea1e20c01 ("program: Match xbl in a/b scenarios")
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-02-27 09:10:12 -08:00
Bjorn Andersson
8589513f65 qdl: Wait for missing EDL device
Add support back for waiting for an EDL device to appear. This is useful
when paired with some automation scripts that introduces "arbitrary"
delays in the process of entering EDL mode.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-01-07 21:20:25 -08:00
Bjorn Andersson
ba86b03391 qdl: Remove debug print for claim return code
The USB claim return code was printed to the console for debug purposes,
remove this.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-01-07 21:05:47 -08:00
Bjorn Andersson
59717efcd4 firehose: Remove unnecessary nop request
The nop was added to mitigate the fact that there's a rather long delay
in the response when sending out the first command. Increasing the write
timeout removes this problem and it's possible to just send the
configure as the first command without issues.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-01-07 21:00:42 -08:00
Bjorn Andersson
5ea1e20c01 program: Match xbl in a/b scenarios
On devices implementing a/b updates the xbl partiiton will be named
xbl_a and xbl_b, extend the match for finding the bootable partition to
support this.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-01-07 16:41:39 -08:00
Bjorn Andersson
826a5cd4fd firehose: Send ZLP after each chunk
It was assumed that we should send all the data following a "program"
request to the device, before sending a ZLP. But on SDM845 it's seen
that not sending a ZLP after each chunk sometimes causes the
communication to stall.

Given that the "program" request already carries the information about
how much data will be transferred there should be no issues with sending
additional ZLPs, so do this.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-01-07 16:36:54 -08:00
Bjorn Andersson
8f7987f756 qdl: Communicate using USBFS instead of qcserial
On some newer platforms the device ignore the configure request to
disable ZLP, causing the Firehose program request to stall, when the
device is waiting for a ZLP to end a transfer.

Mitigate this by circumventing the qcserial driver and drive the USB
traffic directly using USBFS. The tool will attempt to detach qcserial
from the device, in case it's already attached, so no changes are needed
in the kernel or system configuration.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2019-01-07 16:36:50 -08:00
Laxman
b338928519 firehose: support for emmc storage
Added qdl support for emmc storage on platforms with UFS support.  Use
option --s emmc or ufs as a argument to qdl command, if not specified
any option the default storage would be ufs

Tested-by: Nicolas Dechesne <nicolas.dechesne@linaro.org>
Signed-off-by: Laxman <itsmelaxman91@gmail.com>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-11-07 21:33:44 -08:00
Daniel Kutik
dc61f8f79e Moved attr_as_unsigned and attr_as_string to util
Moved the two functions to util.c to remove duplicate code.
The previous error handling in some of the implemenations was
incomplete as it caused qdl to crash.
While the variable errors was incremented we still tried to
return the regular result. Now returning 0/NULL in case of error.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-11-05 09:10:00 -08:00
Daniel Kutik
cfce0beeab program: dropped unused attributes
The attributes size_in_KB, sparse and start_byte_hex
are not used and seem to be optional. Some program
xml files do not always contain them which then causes
qdl to crash. Simply removing the unused attributes
fixes this problem.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-11-05 09:09:36 -08:00
Niklas Cassel
a50ec8047c qdl: fix qdl when building for 32-bit
The sahara protocol specification defines the image field
for a read64 request as 8 bytes.
Use the correct type to represent this.

This will not change the behavior when building for 64-bit,
where the compiler already aligned the offset field correctly,
in order to satisfy natural alignment requirements.

However, when building for 32-bit, this change results in a
qdl that can flash a device successfully.

Signed-off-by: Niklas Cassel <niklas.cassel@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-06-12 12:08:01 -07:00
Niklas Cassel
5fc4cdbba4 qdl: fix error message referring to ttyUSB1
Since it is by no means certain that the current tty is ttyUSB1,
do not assume it to be so.

Unfortunately we do not know the current tty in main().
It would be possible to refactor the code so that we could print
the current tty, but since the only consumer of that information
would be this error message, that refactoring seems unjustified.

Signed-off-by: Niklas Cassel <niklas.cassel@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-06-12 12:07:35 -07:00
Niklas Cassel
37edf318b2 qdl: remove superfluous assignment
This assignment is superfluous, since the same assignment is performed
in the for loop's initializer.

Signed-off-by: Niklas Cassel <niklas.cassel@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-06-12 12:07:28 -07:00
Niklas Cassel
d9935e1f54 qdl: use correct printf modifier for uint64_t
printf uses PRIx64 modifier to print uint64_t.

This modifier has to be used outside of double-quotes.

Fixes build warnings on 32-bit systems, e.g. ARMv7.

Signed-off-by: Niklas Cassel <niklas.cassel@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-05-28 22:45:17 -07:00
Niklas Cassel
d77b106a57 qdl: use correct printf modifier for size_t
printf uses z modifier to print size_t.

Also change d modifier to u, since size_t is unsigned.
(ssize_t is the signed version.)

Fixes build warning on 32-bit systems, e.g. ARMv7.

Signed-off-by: Niklas Cassel <niklas.cassel@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-05-28 22:45:15 -07:00
Nicolas Dechesne
45cc3521d8 qdl: add --include to specific optional folder to look for files
Let the programmer search for files beyond the current folder. When --include is
used , the programmmer will first look for files in the specified folder, and it
will then fallback to looking at the current folder.

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-04-18 22:07:36 -07:00
Nicolas Dechesne
5d51472f1c Merge ".gitreview: add new file" 2018-04-06 07:50:40 +00:00
Tanya Finkel
df842101b1 QDL: Fix UFS provision issue
Fix the return value verification and add printf status message

Signed-off-by: Tanya Finkel <tfinkel@codeaurora.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-02-28 14:13:59 -08:00
Bjorn Andersson
44a80b1266 qdl: Remove possibility for uninitialized variable
In the case that we find a "data" tag, but not a "program" or "ufs"
child node type might have been left unitialized. Fix this by
initializing type.

Also fix up the styling of the multiline blocks.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-02-21 22:36:03 -08:00
Kirill Kapranov
d2e791a950 QDL/firehose: Add UFS provisioning functionality
Add UFS provisioning functionality using Firehose.

Signed-off-by: Kirill Kapranov <kkapra@codeaurora.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-02-21 22:29:15 -08:00
Kirill Kapranov
b4c2e8f267 qdl: fix 'usage' message, add missing key 'debug'
Add a mention of command line parameter '--debug' in 'usage' printout

Signed-off-by: Kirill Kapranov <kkapra@codeaurora.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-02-05 09:52:22 -08:00
Nicolas Dechesne
7be48f4fc9 qdl: implement args processing with getopt_long
we preserve the same args as before, however it should now be simpler to
add new options.

Signed-off-by: Nicolas Dechesne <nicolas.dechesne@linaro.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-02-02 10:02:56 -08:00
Kirill Kapranov
19e8a2d4ba firehose: Add missing xmlFreeDoc
Each xmlNewDoc should have a matching xmlFreeDoc call in order to avoid
memory leaks.

Signed-off-by: Kirill Kapranov <kkapra@codeaurora.org>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2018-02-01 22:07:43 -08:00
Nicolas Dechesne
1fc3c04d17 firehose: Don't truncate partitions to 0 sectors
Some program entries has num_partition_sectors=0 but still specifies a
file, don't truncate the size of the file in this case.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2017-11-30 21:38:36 -08:00
Bjorn Andersson
0251833b4d program: Skip entries without a valid filename
Follow the behavior of the other flash tools and skip partitions with no
filename, instead of filling them with zeros. This reduces the flash
time considerably for some set of xml files.

Also clean up firehose_program() as we no longer need to support calling
this function with an invalid fd.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2017-11-29 21:13:10 -08:00
Bjorn Andersson
febbbcc32e firehose: Negotiate max payload size
When the host propose a larger payload size than the device accepts the
device will respond with a NACK, containing the maximum payload size.
Similarily the ACK will contain the supported max if the host requests a
lower value than the device supports.

In both cases we pick the largest possible value and send a second
configure message to select this payload size.

Reported-by: Kirill Kapranov <c_kkapra@qti.qualcomm.com>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
2017-11-29 13:24:29 -08:00
Nicolas Dechesne
922699c4db .gitreview: add new file
Convenient file to be used with git-review to make it simpler to contribute
patches into Linaro Gerrit review.

Change-Id: Ia96d2fea0de031e67f03c4384fb20dc9d5696c28
Signed-off-by: Nicolas Dechesne <nicolas.dechesne@linaro.org>
2017-08-29 13:56:05 +02:00
22 changed files with 2206 additions and 517 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

4
.gitreview Normal file
View File

@@ -0,0 +1,4 @@
[gerrit]
host=review.linaro.org
port=29418
project=landing-teams/working/qualcomm/qdl

View File

@@ -1,17 +1,40 @@
OUT := qdl
QDL := qdl
RAMDUMP := qdl-ramdump
CFLAGS := -O2 -Wall -g `xml2-config --cflags`
LDFLAGS := `xml2-config --libs`
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
SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c
OBJS := $(SRCS:.c=.o)
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)
$(OUT): $(OBJS)
RAMDUMP_SRCS := ramdump.c sahara.c usb.c util.c
RAMDUMP_OBJS := $(RAMDUMP_SRCS:.c=.o)
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)
clean:
rm -f $(OUT) $(OBJS)
$(RAMDUMP): $(RAMDUMP_OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
install: $(OUT)
install -D -m 755 $< $(DESTDIR)$(prefix)/bin/$<
$(KS_OUT): $(KS_OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
compile_commands.json: $(QDL_SRCS) $(KS_SRCS)
@echo -n $^ | jq -snR "[inputs|split(\" \")[]|{directory:\"$(PWD)\", command: \"$(CC) $(CFLAGS) -c \(.)\", file:.}]" > $@
clean:
rm -f $(QDL) $(QDL_OBJS)
rm -f $(RAMDUMP) $(RAMDUMP_OBJS)
rm -f $(KS_OUT) $(KS_OBJS)
rm -f compile_commands.json
install: $(QDL) $(RAMDUMP) $(KS_OUT)
install -d $(DESTDIR)$(prefix)/bin
install -m 755 $^ $(DESTDIR)$(prefix)/bin

6
README
View File

@@ -9,8 +9,8 @@ Usage:
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

File diff suppressed because it is too large Load Diff

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

28
patch.c
View File

@@ -30,36 +30,16 @@
*/
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "patch.h"
#include "qdl.h"
static struct patch *patches;
static struct patch *patches_last;
static unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors)
{
xmlChar *value;
value = xmlGetProp(node, (xmlChar*)attr);
if (!value)
(*errors)++;
return strtoul((char*)value, NULL, 10);
}
static const char *attr_as_string(xmlNode *node, const char *attr, int *errors)
{
xmlChar *value;
value = xmlGetProp(node, (xmlChar*)attr);
if (!value)
(*errors)++;
return strdup((char*)value);
}
int patch_load(const char *patch_file)
{
struct patch *patch;
@@ -117,7 +97,7 @@ int patch_load(const char *patch_file)
return 0;
}
int patch_execute(int fd, int (*apply)(int fd, struct patch *patch))
int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct patch *patch))
{
struct patch *patch;
int ret;
@@ -126,7 +106,7 @@ int patch_execute(int fd, int (*apply)(int fd, struct patch *patch))
if (strcmp(patch->filename, "DISK"))
continue;
ret = apply(fd, patch);
ret = apply(qdl, patch);
if (ret)
return ret;
}

View File

@@ -1,6 +1,8 @@
#ifndef __PATCH_H__
#define __PATCH_H__
struct qdl_device;
struct patch {
unsigned sector_size;
unsigned byte_offset;
@@ -15,6 +17,6 @@ struct patch {
};
int patch_load(const char *patch_file);
int patch_execute(int fd, int (*apply)(int fd, struct patch *patch));
int patch_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct patch *patch));
#endif

232
program.c
View File

@@ -31,58 +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;
static unsigned attr_as_unsigned(xmlNode *node, const char *attr, int *errors)
{
xmlChar *value;
value = xmlGetProp(node, (xmlChar*)attr);
if (!value)
(*errors)++;
return strtoul((char*)value, NULL, 10);
}
static const char *attr_as_string(xmlNode *node, const char *attr, int *errors)
{
xmlChar *value;
value = xmlGetProp(node, (xmlChar*)attr);
if (!value)
(*errors)++;
if (value && value[0] == '\0')
return NULL;
return strdup((char*)value);
}
static bool attr_as_bool(xmlNode *node, const char *attr, int *errors)
{
xmlChar *value;
value = xmlGetProp(node, (xmlChar*)attr);
if (!value)
(*errors)++;
return xmlStrcmp(value, (xmlChar*)"true") == 0;
}
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) {
@@ -95,62 +140,58 @@ 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->size = attr_as_unsigned(node, "size_in_KB", &errors);
program->sparse = attr_as_bool(node, "sparse", &errors);
program->start_bytes = attr_as_string(node, "start_byte_hex", &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(int usbfd, int (*apply)(int usbfd, struct program *program, int fd))
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
const char *incdir, bool allow_missing)
{
struct program *program;
const char *filename;
char tmp[PATH_MAX];
int ret;
int fd;
for (program = programes; program; program = program->next) {
fd = -1;
if (program->filename) {
fd = open(program->filename, O_RDONLY);
if (fd < 0) {
printf("Unable to open %s...ignoring\n", program->filename);
continue;
}
if (program->is_erase || !program->filename)
continue;
filename = program->filename;
if (incdir) {
snprintf(tmp, PATH_MAX, "%s/%s", incdir, filename);
if (access(tmp, F_OK) != -1)
filename = tmp;
}
ret = apply(usbfd, program, fd);
fd = open(filename, O_RDONLY);
if (fd < 0) {
printf("Unable to open %s", program->filename);
if (!allow_missing) {
printf("...failing\n");
return -1;
}
printf("...ignoring\n");
continue;
}
ret = apply(qdl, program, fd);
close(fd);
if (ret)
@@ -160,14 +201,32 @@ int program_execute(int usbfd, int (*apply)(int usbfd, struct program *program,
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
*
* Returns partition number, or negative errno on failure.
*
* Scan program tags for a partition with the label "xbl" or "sbl1" and return
* the partition number for this. If more than one line matches we're assuming
* our logic is flawed and return an error.
* 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.
*/
int program_find_bootable_partition(void)
{
@@ -177,8 +236,11 @@ 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, "sbl1")) {
if (!strcmp(label, "xbl") || !strcmp(label, "xbl_a") ||
!strcmp(label, "sbl1")) {
if (part != -ENOENT)
return -EINVAL;

View File

@@ -2,24 +2,29 @@
#define __PROGRAM_H__
#include <stdbool.h>
#include "qdl.h"
struct program {
unsigned pages_per_block;
unsigned sector_size;
unsigned file_offset;
const char *filename;
const char *label;
unsigned num_sectors;
unsigned partition;
unsigned size;
bool sparse;
const char *start_bytes;
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_execute(int usbfd, int (*apply)(int usbfd, struct program *program, int fd));
int program_load(const char *program_file, bool is_nand);
int program_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program, int fd),
const char *incdir, bool allow_missing);
int erase_execute(struct qdl_device *qdl, int (*apply)(struct qdl_device *qdl, struct program *program));
int program_find_bootable_partition(void);
#endif

299
qdl.c
View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2016-2017, Linaro Ltd.
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,41 +29,40 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <getopt.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"
#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)
{
xmlNode *root;
xmlDoc *doc;
int type;
xmlNode *node;
int type = QDL_FILE_UNKNOWN;
doc = xmlReadFile(xml_file, NULL, 0);
if (!doc) {
@@ -71,196 +71,151 @@ static int detect_type(const char *xml_file)
}
root = xmlDocGetRootElement(doc);
if (!xmlStrcmp(root->name, (xmlChar*)"patches"))
if (!xmlStrcmp(root->name, (xmlChar*)"patches")) {
type = QDL_FILE_PATCH;
else if (!xmlStrcmp(root->name, (xmlChar*)"data"))
type = QDL_FILE_PROGRAM;
else if (!xmlStrcmp(root->name, (xmlChar*)"contents"))
} else if (!xmlStrcmp(root->name, (xmlChar*)"data")) {
for (node = root->children; node ; node = node->next) {
if (node->type != XML_ELEMENT_NODE)
continue;
if (!xmlStrcmp(node->name, (xmlChar*)"program")) {
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;
}
}
} else if (!xmlStrcmp(root->name, (xmlChar*)"contents")) {
type = QDL_FILE_CONTENTS;
else
type = QDL_FILE_UNKNOWN;
}
xmlFreeDoc(doc);
return type;
}
static int readat(int dir, const char *name, char *buf, size_t len)
static void print_usage(void)
{
ssize_t n;
int fd;
int ret = 0;
fd = openat(dir, name, O_RDONLY);
if (fd < 0)
return fd;
n = read(fd, buf, len - 1);
if (n < 0) {
warn("failed to read %s", name);
ret = -EINVAL;
goto close_fd;
}
buf[n] = '\0';
buf[strcspn(buf, "\n")] = '\0';
close_fd:
close(fd);
return ret;
extern const char *__progname;
fprintf(stderr,
"%s [--debug] [--allow-missing] [--storage <emmc|nand|ufs>] [--finalize-provisioning] [--include <PATH>] [--serial <NUM>] [--out-chunk-size <SIZE>] <prog.mbn> [<program> <patch> ...]\n",
__progname);
}
static int find_qdl_tty(char *dev_name, size_t dev_name_len)
{
struct dirent *de;
int found = -ENOENT;
char vid[5];
char pid[5];
DIR *dir;
int tty;
int fd;
int ret;
tty = open("/sys/class/tty", O_DIRECTORY);
if (tty < 0)
err(1, "failed to open /sys/class/tty");
dir = fdopendir(tty);
if (!dir)
err(1, "failed to opendir /sys/class/tty");
while ((de = readdir(dir)) != NULL) {
if (strncmp(de->d_name, "ttyUSB", 6) != 0)
continue;
fd = openat(tty, de->d_name, O_DIRECTORY);
if (fd < 0)
continue;
ret = readat(fd, "../../../../idVendor", vid, sizeof(vid));
if (ret < 0)
goto close_fd;
ret = readat(fd, "../../../../idProduct", pid, sizeof(pid));
if (ret < 0)
goto close_fd;
if (strcmp(vid, "05c6") || strcmp(pid, "9008"))
goto close_fd;
snprintf(dev_name, dev_name_len, "/dev/%s", de->d_name);
found = 0;
close_fd:
close(fd);
}
closedir(dir);
close(tty);
return found;
}
static int tty_open(struct termios *old)
{
struct termios tios;
char path[PATH_MAX];
int ret;
int fd;
retry:
ret = find_qdl_tty(path, sizeof(path));
if (ret < 0) {
printf("Waiting for QDL tty...\r");
fflush(stdout);
sleep(1);
goto retry;
}
fd = open(path, O_RDWR | O_NOCTTY | O_EXCL);
if (fd < 0) {
err(1, "unable to open \"%s\"", path);
}
ret = tcgetattr(fd, old);
if (ret < 0)
err(1, "unable to retrieve \"%s\" tios", path);
memset(&tios, 0, sizeof(tios));
tios.c_cflag = B115200 | CRTSCTS | CS8 | CLOCAL | CREAD;
tios.c_iflag = IGNPAR;
tios.c_oflag = 0;
tcflush(fd, TCIFLUSH);
ret = tcsetattr(fd, TCSANOW, &tios);
if (ret < 0)
err(1, "unable to update \"%s\" tios", path);
return fd;
}
enum {
OPT_OUT_CHUNK_SIZE = 1000,
};
int main(int argc, char **argv)
{
extern const char *__progname;
struct termios tios;
char *prog_mbn;
char *prog_mbn, *storage="ufs";
char *incdir = NULL;
char *serial = NULL;
int type;
int ret;
int fd;
int i;
int opt;
bool qdl_finalize_provisioning = false;
bool allow_missing = false;
long out_chunk_size;
if (argc >= 2 && strcmp(argv[1], "--debug") == 0) {
qdl_debug = true;
argv++;
argc--;
}
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}
};
if (argc < 3) {
fprintf(stderr, "%s <prog.mbn> [<program> <patch> ...]\n", __progname);
return 1;
}
prog_mbn = argv[1];
for (i = 2; i < argc; i++) {
type = detect_type(argv[i]);
if (type < 0 || type == QDL_FILE_UNKNOWN)
errx(1, "failed to detect file type of %s\n", argv[i]);
switch (type) {
case QDL_FILE_PATCH:
ret = patch_load(argv[i]);
if (ret < 0)
errx(1, "patch_load %s failed", argv[i]);
while ((opt = getopt_long(argc, argv, "dfi:S:", options, NULL )) != -1) {
switch (opt) {
case 'd':
qdl_debug = true;
break;
case QDL_FILE_PROGRAM:
ret = program_load(argv[i]);
if (ret < 0)
errx(1, "program_load %s failed", argv[i]);
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:
errx(1, "%s type not yet supported", argv[i]);
break;
print_usage();
return 1;
}
}
fd = tty_open(&tios);
if (fd < 0)
err(1, "failed to open QDL tty");
/* at least 2 non optional args required */
if ((optind + 2) > argc) {
print_usage();
return 1;
}
ret = sahara_run(fd, prog_mbn);
prog_mbn = argv[optind++];
do {
type = detect_type(argv[optind]);
if (type < 0 || type == QDL_FILE_UNKNOWN)
errx(1, "failed to detect file type of %s\n", argv[optind]);
switch (type) {
case QDL_FILE_PATCH:
ret = patch_load(argv[optind]);
if (ret < 0)
errx(1, "patch_load %s failed", argv[optind]);
break;
case QDL_FILE_PROGRAM:
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)
errx(1, "ufs_load %s failed", argv[optind]);
break;
default:
errx(1, "%s type not yet supported", argv[optind]);
break;
}
} while (++optind < argc);
ret = qdl_open(&qdl, serial);
if (ret)
return 1;
qdl.mappings[0] = prog_mbn;
ret = sahara_run(&qdl, qdl.mappings, true, NULL, NULL);
if (ret < 0)
goto out;
return 1;
ret = firehose_run(fd);
out:
ret = tcsetattr(fd, TCSANOW, &tios);
ret = firehose_run(&qdl, incdir, storage, allow_missing);
if (ret < 0)
warn("unable to restore tios of ttyUSB1");
close(fd);
return 1;
return 0;
}

32
qdl.h
View File

@@ -5,10 +5,38 @@
#include "patch.h"
#include "program.h"
#include "read.h"
#include <libxml/tree.h>
int firehose_run(int fd);
int sahara_run(int fd, char *prog_mbn);
#define MAPPING_SZ 64
struct libusb_device_handle;
struct qdl_device {
struct libusb_device_handle *usb_handle;
int fd;
int in_ep;
int out_ep;
size_t in_maxpktsize;
size_t out_maxpktsize;
size_t out_chunk_size;
char *mappings[MAPPING_SZ]; // array index is the id from the device
};
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);
extern bool qdl_debug;

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

363
sahara.c
View File

@@ -35,6 +35,8 @@
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <inttypes.h>
#include <poll.h>
#include <stdbool.h>
#include <stdint.h>
@@ -45,6 +47,49 @@
#include <unistd.h>
#include "qdl.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;
uint32_t length;
@@ -77,43 +122,60 @@ struct sahara_pkt {
uint32_t status;
} done_resp;
struct {
uint32_t image;
uint64_t addr;
uint64_t length;
} debug64_req;
struct {
uint64_t image;
uint64_t offset;
uint64_t length;
} read64_req;
};
};
static void sahara_hello(int fd, struct sahara_pkt *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;
assert(pkt->length == 0x30);
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 == 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;
write(fd, &resp, resp.length);
qdl_write(qdl, &resp, resp.length);
}
static int sahara_read_common(int fd, 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;
@@ -125,50 +187,96 @@ static int sahara_read_common(int fd, const char *mbn, off_t offset, size_t len)
goto out;
}
n = write(fd, buf, n);
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(int fd, 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(fd, 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(int fd, 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: %d offset: 0x%lx length: 0x%lx\n",
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(fd, 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(int fd, struct sahara_pkt *pkt)
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);
@@ -177,68 +285,203 @@ static void sahara_eoi(int fd, struct sahara_pkt *pkt)
return;
}
done.cmd = 5;
done.length = 0x8;
write(fd, &done, done.length);
done.cmd = SAHARA_DONE_CMD;
done.length = SAHARA_DONE_LENGTH;
qdl_write(qdl, &done, done.length);
}
static int sahara_done(int fd, struct sahara_pkt *pkt)
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(int fd, 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;
struct pollfd pfd;
int ramdump_dir = -1;
char buf[4096];
char tmp[32];
bool done = false;
int n;
while (!done) {
pfd.fd = fd;
pfd.events = POLLIN;
n = poll(&pfd, 1, -1);
if (n < 0) {
warn("failed to poll");
break;
}
if (ramdump_path) {
ramdump_dir = open(ramdump_path, O_DIRECTORY);
if (ramdump_dir < 0)
err(1, "failed to open directory for ramdump output");
}
n = read(fd, buf, sizeof(buf));
if (n == 0) {
continue;
} else if (n < 0) {
warn("failed to read");
while (!done) {
n = qdl_read(qdl, buf, sizeof(buf), 1000);
if (n < 0)
break;
}
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:
sahara_hello(fd, pkt);
case SAHARA_HELLO_CMD:
sahara_hello(qdl, pkt);
break;
case 3:
sahara_read(fd, pkt, prog_mbn);
case SAHARA_READ_DATA_CMD:
sahara_read(qdl, pkt, img_arr, single_image);
break;
case 4:
sahara_eoi(fd, pkt);
case SAHARA_END_OF_IMAGE_CMD:
sahara_eoi(qdl, pkt);
break;
case 6:
sahara_done(fd, 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(fd, 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);
@@ -247,5 +490,7 @@ int sahara_run(int fd, char *prog_mbn)
}
}
return 0;
close(ramdump_dir);
return done ? 0 : -1;
}

317
ufs.c Normal file
View File

@@ -0,0 +1,317 @@
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include "ufs.h"
#include "qdl.h"
#include "patch.h"
struct ufs_common *ufs_common_p;
struct ufs_epilogue *ufs_epilogue_p;
struct ufs_body *ufs_body_p;
struct ufs_body *ufs_body_last;
static const char notice_bconfigdescrlock[] = "\n"
"Please pay attention that UFS provisioning is irreversible (OTP) operation unless parameter bConfigDescrLock = 0.\n"
"In order to prevent unintentional device locking the tool has the following safety:\n\n"
" if you REALLY intend to perform OTP, please ensure that your XML includes property\n"
" bConfigDescrLock = 1 AND provide command line parameter --finalize-provisioning.\n\n"
" Unless you intend to lock your device, please set bConfigDescrLock = 0 in your XML\n"
" and don't use command line parameter --finalize-provisioning.\n\n"
"In case of mismatch between CL and XML provisioning is not performed.\n\n";
bool ufs_need_provisioning(void)
{
return !!ufs_epilogue_p;
}
struct ufs_common *ufs_parse_common_params(xmlNode *node, bool finalize_provisioning)
{
struct ufs_common *result;
int errors;
result = calloc(1, sizeof(struct ufs_common));
errors = 0;
result->bNumberLU = attr_as_unsigned(node, "bNumberLU", &errors);
result->bBootEnable = !!attr_as_unsigned(node, "bBootEnable", &errors);
result->bDescrAccessEn = !!attr_as_unsigned(node, "bDescrAccessEn", &errors);
result->bInitPowerMode = attr_as_unsigned(node, "bInitPowerMode", &errors);
result->bHighPriorityLUN = attr_as_unsigned(node, "bHighPriorityLUN", &errors);
result->bSecureRemovalType = attr_as_unsigned(node, "bSecureRemovalType", &errors);
result->bInitActiveICCLevel = attr_as_unsigned(node, "bInitActiveICCLevel", &errors);
result->wPeriodicRTCUpdate = attr_as_unsigned(node, "wPeriodicRTCUpdate", &errors);
result->bConfigDescrLock = !!attr_as_unsigned(node, "bConfigDescrLock", &errors);
if (errors) {
fprintf(stderr, "[UFS] errors while parsing common\n");
free(result);
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;
}
struct ufs_body *ufs_parse_body(xmlNode *node)
{
struct ufs_body *result;
int errors;
result = calloc(1, sizeof(struct ufs_body));
errors = 0;
result->LUNum = attr_as_unsigned(node, "LUNum", &errors);
result->bLUEnable = !!attr_as_unsigned(node, "bLUEnable", &errors);
result->bBootLunID = attr_as_unsigned(node, "bBootLunID", &errors);
result->size_in_kb = attr_as_unsigned(node, "size_in_kb", &errors);
result->bDataReliability = attr_as_unsigned(node, "bDataReliability", &errors);
result->bLUWriteProtect = attr_as_unsigned(node, "bLUWriteProtect", &errors);
result->bMemoryType = attr_as_unsigned(node, "bMemoryType", &errors);
result->bLogicalBlockSize = attr_as_unsigned(node, "bLogicalBlockSize", &errors);
result->bProvisioningType = attr_as_unsigned(node, "bProvisioningType", &errors);
result->wContextCapabilities = attr_as_unsigned(node, "wContextCapabilities", &errors);
result->desc = attr_as_string(node, "desc", &errors);
if (errors) {
fprintf(stderr, "[UFS] errors while parsing body\n");
free(result);
return NULL;
}
return result;
}
struct ufs_epilogue *ufs_parse_epilogue(xmlNode *node)
{
struct ufs_epilogue *result;
int errors = 0;
result = calloc(1, sizeof(struct ufs_epilogue));
result->LUNtoGrow = attr_as_unsigned(node, "LUNtoGrow", &errors);
if (errors) {
fprintf(stderr, "[UFS] errors while parsing epilogue\n");
free(result);
return NULL;
}
return result;
}
int ufs_load(const char *ufs_file, bool finalize_provisioning)
{
xmlNode *node;
xmlNode *root;
xmlDoc *doc;
int retval = 0;
struct ufs_body *ufs_body_tmp;
if (ufs_common_p) {
fprintf(stderr,
"Only one UFS provisioning XML allowed, %s ignored\n",
ufs_file);
return -EEXIST;
}
doc = xmlReadFile(ufs_file, NULL, 0);
if (!doc) {
fprintf(stderr, "[UFS] failed to parse %s\n", ufs_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*)"ufs")) {
fprintf(stderr, "[UFS] unrecognized tag \"%s\", ignoring\n",
node->name);
continue;
}
if (xmlGetProp(node, (xmlChar *)"bNumberLU")) {
if (!ufs_common_p) {
ufs_common_p = ufs_parse_common_params(node,
finalize_provisioning);
}
else {
fprintf(stderr, "[UFS] Only one common tag is allowed\n"
"[UFS] provisioning aborted\n");
retval = -EINVAL;
break;
}
if (!ufs_common_p) {
fprintf(stderr, "[UFS] Common tag corrupted\n"
"[UFS] provisioning aborted\n");
retval = -EINVAL;
break;
}
} else if (xmlGetProp(node, (xmlChar *)"LUNum")) {
ufs_body_tmp = ufs_parse_body(node);
if(ufs_body_tmp) {
if (ufs_body_p) {
ufs_body_last->next = ufs_body_tmp;
ufs_body_last = ufs_body_tmp;
}
else {
ufs_body_p = ufs_body_tmp;
ufs_body_last = ufs_body_tmp;
}
}
else {
fprintf(stderr, "[UFS] LU tag corrupted\n"
"[UFS] provisioning aborted\n");
retval = -EINVAL;
break;
}
} else if (xmlGetProp(node, (xmlChar *)"commit")) {
if (!ufs_epilogue_p) {
ufs_epilogue_p = ufs_parse_epilogue(node);
if (ufs_epilogue_p)
continue;
}
else {
fprintf(stderr, "[UFS] Only one finalizing tag is allowed\n"
"[UFS] provisioning aborted\n");
retval = -EINVAL;
break;
}
if (!ufs_epilogue_p) {
fprintf(stderr, "[UFS] Finalizing tag corrupted\n"
"[UFS] provisioning aborted\n");
retval = -EINVAL;
break;
}
} else {
fprintf(stderr, "[UFS] Unknown tag or %s corrupted\n"
"[UFS] provisioning aborted\n", ufs_file);
retval = -EINVAL;
break;
}
}
xmlFreeDoc(doc);
if (!retval && (!ufs_common_p || !ufs_body_p || !ufs_epilogue_p)) {
fprintf(stderr, "[UFS] %s seems to be incomplete\n"
"[UFS] provisioning aborted\n", ufs_file);
retval = -EINVAL;
}
if (retval){
if (ufs_common_p) {
free(ufs_common_p);
}
if (ufs_body_p) {
free(ufs_body_p);
}
if (ufs_epilogue_p) {
free(ufs_epilogue_p);
}
fprintf(stderr, "[UFS] %s seems to be corrupted, ignore\n", ufs_file);
return retval;
}
if (!finalize_provisioning != !ufs_common_p->bConfigDescrLock) {
fprintf(stderr,
"[UFS] Value bConfigDescrLock %d in file %s don't match command line parameter --finalize-provisioning %d\n"
"[UFS] provisioning aborted\n",
ufs_common_p->bConfigDescrLock, ufs_file, finalize_provisioning);
fprintf(stderr, notice_bconfigdescrlock);
return -EINVAL;
}
return 0;
}
int ufs_provisioning_execute(struct qdl_device *qdl,
int (*apply_ufs_common)(struct qdl_device *, struct ufs_common*),
int (*apply_ufs_body)(struct qdl_device *, struct ufs_body*),
int (*apply_ufs_epilogue)(struct qdl_device *, struct ufs_epilogue*, bool))
{
int ret;
struct ufs_body *body;
if (ufs_common_p->bConfigDescrLock) {
int i;
printf("Attention!\nIrreversible provisioning will start in 5 s\n");
for(i=5; i>0; i--) {
printf(".\a");
sleep(1);
}
printf("\n");
}
// Just ask a target to check the XML w/o real provisioning
ret = apply_ufs_common(qdl, ufs_common_p);
if (ret)
return ret;
for (body = ufs_body_p; body; body = body->next) {
ret = apply_ufs_body(qdl, body);
if (ret)
return ret;
}
ret = apply_ufs_epilogue(qdl, ufs_epilogue_p, false);
if (ret) {
fprintf(stderr,
"UFS provisioning impossible, provisioning XML may be corrupted\n");
return ret;
}
// Real provisioning -- target didn't refuse a given XML
ret = apply_ufs_common(qdl, ufs_common_p);
if (ret)
return ret;
for (body = ufs_body_p; body; body = body->next) {
ret = apply_ufs_body(qdl, body);
if (ret)
return ret;
}
return apply_ufs_epilogue(qdl, ufs_epilogue_p, true);
}

79
ufs.h Normal file
View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of The Linux Foundation 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 "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 __UFS_H__
#define __UFS_H__
#include <stdbool.h>
struct qdl_device;
struct ufs_common {
unsigned bNumberLU;
bool bBootEnable;
bool bDescrAccessEn;
unsigned bInitPowerMode;
unsigned bHighPriorityLUN;
unsigned bSecureRemovalType;
unsigned bInitActiveICCLevel;
unsigned wPeriodicRTCUpdate;
bool bConfigDescrLock;
bool wb;
bool bWriteBoosterBufferPreserveUserSpaceEn;
bool bWriteBoosterBufferType;
unsigned shared_wb_buffer_size_in_kb;
};
struct ufs_body {
unsigned LUNum;
bool bLUEnable;
unsigned bBootLunID;
unsigned size_in_kb;
unsigned bDataReliability;
unsigned bLUWriteProtect;
unsigned bMemoryType;
unsigned bLogicalBlockSize;
unsigned bProvisioningType;
unsigned wContextCapabilities;
const char *desc;
struct ufs_body *next;
};
struct ufs_epilogue {
unsigned LUNtoGrow;
bool commit;
};
int ufs_load(const char *ufs_file, bool finalize_provisioning);
int ufs_provisioning_execute(struct qdl_device *qdl,
int (*apply_ufs_common)(struct qdl_device *qdl, struct ufs_common *ufs),
int (*apply_ufs_body)(struct qdl_device *qdl, struct ufs_body *ufs),
int (*apply_ufs_epilogue)(struct qdl_device *qdl, struct ufs_epilogue *ufs, bool commit));
bool ufs_need_provisioning(void);
#endif

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