* [PATCH v9 0/7] Qualcomm Sahara protocol enhancements
@ 2026-10-13 8:45 Kishore Batta
2026-10-13 8:45 ` [PATCH v9 1/7] Add documentation for Sahara protocol Kishore Batta
` (6 more replies)
0 siblings, 7 replies; 14+ messages in thread
From: Kishore Batta @ 2026-10-13 8:45 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Jeff Hugo, Carl Vanderlip,
Oded Gabbay, Manivannan Sadhasivam, Randy Dunlap
Cc: linux-doc, linux-kernel, linux-arm-msm, dri-devel, mhi,
Kishore Batta
Hi All,
This series reworks the Sahara protocol driver to make it reusable for
multiple MHI based devices and adds support for capturing, restoring and
exposing DDR training data using the Sahara command mode.
The Sahara protocol is transported over the MHI bus and is used by multiple
flashless devices to transfer firmware images, retrieve memory dumps and
exchange command mode data during early boot. However, the current
implementation lives under the QAIC accelerator driver and contains
device-specific assumptions that limit reuse.
Some MHI devices (for example, QDU100) expose the sahara protocol directly
on a "SAHARA" MHI channel and rely on command mode to exchange DDR training
data with the host. The existing driver does not bind to such devices and
ignores Sahara command mode packets, causing training data to be dropped.
This series addresses these issues by relocating the Sahara driver to the
MHI subsystem, centralizing device specific configuration and adding command
mode handling for DDR training data.
Overview of the changes in this series -
1. Move Sahara under the MHI subsystem:
a. Relocate the sahara protocol driver from QAIC accelerator tree to
drivers/bus/mhi/host/clients directory.
b. Register Sahara as an independent MHI protocol driver.
2. Generalize device matching and configuration
a. Allow the driver to bind to devices exposing the protocol on a
SAHARA MHI channel.
b. Centralize firmware image table selection at probe time using a variant
table, instead of scattered conditionals.
c. Preserve existing behavior on AIC devices.
3. Add QDU100 firmware image table support
a. Add a QDU100 image table and select it based on the matched MHI channel.
b. No separate client driver or registration mechanism is required.
4. Add Sahara command mode support for DDR training.
a. Handle command mode packets(CMD_READY, EXECUTE, EXECUTE_DATA).
b. Query supported commands and retrieve DDR training data from the device.
c. Allocate receive buffers based on the reported payload size and copy
raw data from the MHI DL.
d. Store training data in controller-scoped memory using devres so it
survives sahara channel teardown.
5. Expose DDR training data to userspace
a. Add a read-only binary sysfs attribute under the MHI controller device.
b. The attribute reads directly from controller-scoped storage and remains
available after the Sahara channel device is removed.
c. Cleanup is handled automatically via device-managed resources.
6. Document the sysfs ABI
a. Add ABI documentation describing the DDR training data sysfs node.
Signed-off-by: Kishore Batta <kishore.batta@oss.qualcomm.com>
---
Changes in v9:
- Removed the unnecessary linux/mod_devicetable.h include from the Sahara driver.
- Link to v8: https://lore.kernel.org/r/20261012-sahara_protocol_new_v2-v8-0-e12939d229a1@oss.qualcomm.com
Changes in v8:
- Rebased the series onto the current mhi-next branch so that it applies
cleanly.
- Resolved conflicts with the MHI host client loopback driver and retained
the existing Sahara functionality without functional changes.
Changes in v7:
- Addressed review feedback by correcting documentation punctuation and
removing an unrelated formatting-only change.
- Passed the QDU100 Sahara firmware mapping through struct mhi_pci_dev_info
instead of selecting it with a device-specific conditional in PCI probe.
- Clarified the userspace persistence filename for captured DDR training data:
qcom/<fw_folder>/mdmddr_0x<serial_no>.mbn.
- Link to v6: https://lore.kernel.org/r/20260701-sahara_protocol_new_v2-v6-0-3a78362c4741@oss.qualcomm.com
Changes in v6:
- Used BIN_ATTR_RO() instead of struct bin_attribute.
- Link to v5: https://lore.kernel.org/r/20260416-sahara_protocol_new_v2-v5-0-6aebf005e4ba@oss.qualcomm.com
Changes in v5:
- Sahara protocol documentation is moved to Documentation/mhi/ directory.
- Sahara driver is now moved to drivers/bus/mhi/host/clients/ directory.
- Squashed v4 patch 3 with v4 patch 5.
- Squashed v4 patch 9 with v4 patch 8.
- Firmware image tables are now moved to respective MHI controller files.
- Removed redundant if-else conditions when loading DDR training data image.
- Corrected commit subjects and messages.
- Removed Sahara variant structure and introduced mhi_sahara_fw_table structure.
- Link to v4: https://lore.kernel.org/r/20260319-sahara_protocol_new_v2-v4-0-47ad79308762@oss.qualcomm.com
Changes in v4:
- Updated the series based on review feedback.
- Link to v3: https://lore.kernel.org/r/20260310-sahara_protocol_new_v2-v3-0-994ea4b0d5ad@oss.qualcomm.com
- Link to v3: https://lore.kernel.org/r/20260310-sahara_protocol_new_v2-v3-0-994ea4b0d5ad@oss.qualcomm.com
Changes in v3:
- Dropped the explicit image table entry for the boot critical image ID and
added a generic image ID based firmware lookup fallback.
- Link to v2: https://lore.kernel.org/r/20260307-sahara_protocol_new_v2-v2-0-29dc748b5e9c@oss.qualcomm.com
Changes in v2:
- Rebased onto latest linux-next tip.
- Reworked commit messages to clearly start with the problem being solved and
end with a technical description of the change.
- Moved the Sahara driver to drivers/bus/mhi instead of drivers/soc/qcom,
reflecting that its an MHI protocol driver rather than a SoC specific driver.
- Removed client side image table registration and consolidated firmware
selection directly in the sahara driver using a probe-time variant
mechanism.
- Ensured each patch is self-contained and does not break the build or runtime
behavior at any intermediate point.
- Simplified state handling and lifetime management to avoid duplicated state
tracking and ad-hoc cleanup.
- Updated sysfs handling to use controller-scoped devres and avoid one-shot
reads or manual teardown.
- Link to v1: https://lore.kernel.org/r/20250825101926.2160554-1-kishore.batta@oss.qualcomm.com
---
Kishore Batta (7):
Add documentation for Sahara protocol
bus: mhi: Move Sahara protocol driver under MHI host client drivers
bus: mhi: Centralize Sahara firmware image table selection at probe time
bus: mhi: Add QDU100 Sahara variant and firmware fallback
bus: mhi: Load DDR training data using device serial number
bus: mhi: Capture DDR training data via command mode
bus: mhi: Expose DDR training data via controller sysfs
.../ABI/testing/sysfs-bus-mhi-ddr_training_data | 19 +
Documentation/mhi/index.rst | 1 +
Documentation/mhi/sahara_protocol.rst | 1241 ++++++++++++++++++++
drivers/accel/qaic/Kconfig | 1 +
drivers/accel/qaic/Makefile | 3 +-
drivers/accel/qaic/mhi_controller.c | 61 +
drivers/accel/qaic/qaic_drv.c | 9 -
drivers/accel/qaic/sahara.h | 10 -
drivers/bus/mhi/host/Makefile | 2 +-
drivers/bus/mhi/host/clients/Kconfig | 6 +
drivers/bus/mhi/host/clients/Makefile | 1 +
drivers/bus/mhi/host/clients/sahara/Kconfig | 15 +
drivers/bus/mhi/host/clients/sahara/Makefile | 2 +
.../qaic => bus/mhi/host/clients/sahara}/sahara.c | 514 ++++++--
drivers/bus/mhi/host/pci_generic.c | 41 +
include/linux/mhi.h | 17 +
16 files changed, 1851 insertions(+), 92 deletions(-)
---
base-commit: 0b1883ca75c0005ab5ca52c7bc1a26329bc39746
change-id: 20260307-sahara_protocol_new_v2-662854773cf7
Best regards,
--
Kishore Batta <kishore.batta@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v9 1/7] Add documentation for Sahara protocol
2026-10-13 8:45 [PATCH v9 0/7] Qualcomm Sahara protocol enhancements Kishore Batta
@ 2026-10-13 8:45 ` Kishore Batta
2026-10-13 8:51 ` sashiko-bot
2026-10-13 8:45 ` [PATCH v9 2/7] bus: mhi: Move Sahara protocol driver under MHI host client drivers Kishore Batta
` (5 subsequent siblings)
6 siblings, 1 reply; 14+ messages in thread
From: Kishore Batta @ 2026-10-13 8:45 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Jeff Hugo, Carl Vanderlip,
Oded Gabbay, Manivannan Sadhasivam, Randy Dunlap
Cc: linux-doc, linux-kernel, linux-arm-msm, dri-devel, mhi,
Kishore Batta
Introduce documentation for the Sahara protocol, describing its
operational modes and their respective functions. The image transfer mode
enables firmware transfer from host to device. The memory debug mode
allows extraction of device memory contents to host. The command mode
facilitates retrieval of DDR training data from the device and also
to restore the training data back to device in subsequent boot of device
to save boot time.
Signed-off-by: Kishore Batta <kishore.batta@oss.qualcomm.com>
---
Documentation/mhi/index.rst | 1 +
Documentation/mhi/sahara_protocol.rst | 1241 +++++++++++++++++++++++++++++++++
2 files changed, 1242 insertions(+)
diff --git a/Documentation/mhi/index.rst b/Documentation/mhi/index.rst
index 0aa00482aa2e2d7ec4941154a8c6947dc0a0ac40..39a38978398b81727514ec95dee4e060a1063b34 100644
--- a/Documentation/mhi/index.rst
+++ b/Documentation/mhi/index.rst
@@ -9,3 +9,4 @@ MHI
mhi
topology
+ sahara_protocol
diff --git a/Documentation/mhi/sahara_protocol.rst b/Documentation/mhi/sahara_protocol.rst
new file mode 100644
index 0000000000000000000000000000000000000000..3190e3e5a2820628e89efcf759c9ca634428f8b2
--- /dev/null
+++ b/Documentation/mhi/sahara_protocol.rst
@@ -0,0 +1,1241 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+
+=============================
+Sahara protocol Specification
+=============================
+
+The Qualcomm Sahara protocol driver is primarily designed for transferring
+software images from a host device to a target device using a simplified data
+transfer mechanism over a link. However, the Sahara protocol does not support
+any authentication/validation of the data sent between devices. Such a mechanism
+is beyond the scope of the protocol.
+
+The Sahara protocol defines two types of packets - Command packet and Data
+packet.
+
+Command packet
+--------------
+ These packets are sent between the host and the target to setup transfers of
+ data packets. The command packets contain a command ID and packet length.
+ Depending on the command, the packet may contain additional command specific
+ field.
+
++-------------+---------------+----------------+----------------+
+| Command ID | Packet length | Optional field | Optional field |
++-------------+---------------+----------------+----------------+
+
+Data packet
+-----------
+ The data packets contain RAW data as shown below.
+
++---------------------------------------------------------+
+| RAW Data (arbitrary number of bytes) |
++---------------------------------------------------------+
+
+Command packet optional fields
+------------------------------
+
++---------+---------------+---------+-----------------------------------------+
+| ID val | Field | Sent by | Description |
++---------+---------------+---------+-----------------------------------------+
+| 0x0 | - | - | Invalid |
++---------+---------------+---------+-----------------------------------------+
+| 0x1 | Hello packet | Target | Initializes connection and protocol |
++---------+---------------+---------+-----------------------------------------+
+| 0x2 | Hello response| Host | Acknowledges connection and protocol |
+| | | | sent by target. Also used to set mode of|
+| | | | operation for target to execute. |
++---------+---------------+---------+-----------------------------------------+
+| 0x3 | Read data | Target | Reads specified number of bytes from |
+| | | | host for a given image. |
++---------+---------------+---------+-----------------------------------------+
+| 0x4 | End of image | Target | Indicates host that the single image tx |
+| | transfer | | is complete. Also used to indicate a |
+| | | | target failure during an image transfer |
++---------+---------------+---------+-----------------------------------------+
+| 0x5 | Done packet | Host | Sends acknowledgment from host that a |
+| | | | single image transfer is complete. |
++---------+---------------+---------+-----------------------------------------+
+| 0x6 | Done response | Target | Provides the following information to |
+| | | | host. |
+| | | | 1. Target is exiting protocol |
+| | | | 2. Whether the target expects to |
+| | | | re-enter protocol to transfer another |
+| | | | image. |
++---------+---------------+---------+-----------------------------------------+
+| 0x7 | Reset packet | Host | Instructs target to perform a reset. |
++---------+---------------+---------+-----------------------------------------+
+| 0x8 | Reset response| Target | Indicates host that target is about to |
+| | | | reset. |
++---------+---------------+---------+-----------------------------------------+
+| 0x9 | Memory debug | Target | Indicates host that target has entered |
+| | packet | | a debug mode where it is ready to |
+| | | | transfer its system memory contents |
++---------+---------------+---------+-----------------------------------------+
+| 0xA | Memory read | Host | Reads specified number of bytes from |
+| | packet | | target's system memory, starting from a |
+| | | | specified address. |
++---------+---------------+---------+-----------------------------------------+
+| 0xB | Command ready | Target | Indicates host that target is ready to |
+| | packet | | receive client commands. |
++---------+---------------+---------+-----------------------------------------+
+| 0xC | Command switch| Host | Indicates target to switch modes. |
+| | mode packet | | 1. Image transfer pending mode. |
+| | | | 2. Image transfer complete mode. |
+| | | | 3. Memory debug mode. |
+| | | | 4. Command mode. |
++---------+---------------+---------+-----------------------------------------+
+| 0xD | Command | Host | Indicates target to execute a given |
+| | execute packet| | client command. |
++---------+---------------+---------+-----------------------------------------+
+| 0xE | Command | Target | Indicates host that target has executed |
+| | execute | | client command. Also used to indicate |
+| | response | | status of executed command. |
+| | packet | | |
++---------+---------------+---------+-----------------------------------------+
+| 0xF | Command | Host | Indicates target that host is ready to |
+| | execute | | receive data resulting from executing |
+| | data | | previous client command. |
+| | packet | | |
++---------+---------------+---------+-----------------------------------------+
+| 0x10 | 64 bit Memory | Target | Indicates host that target has entered |
+| | debug packet | | a debug mode where it is ready to |
+| | | | transfer its 64 bit system memory |
+| | | | contents. |
++---------+---------------+---------+-----------------------------------------+
+| 0x11 | 64 bit Memory | Host | Reads specified number of bytes from |
+| | read packet | | target's system memory, starting from a |
+| | | | 64 bit specified address. |
++---------+---------------+---------+-----------------------------------------+
+| 0x12 | 64 bit Read | Target | Reads specified number of bytes from |
+| | data | | host for a given 64 bit image. |
++---------+---------------+---------+-----------------------------------------+
+| 0x13 | Reset Sahara | Host | Resets Sahara state machine and enters |
+| | sate machine | | Sahara entry without target reset |
+| | packet | | |
++---------+---------------+---------+-----------------------------------------+
+| 0x14 | Write data | Target | Writes specified number of bytes to host|
+| | packet | | for a given image |
++---------+---------------+---------+-----------------------------------------+
+| Others | - | - | Invalid |
++---------+---------------+---------+-----------------------------------------+
+
+
+Hello Packet
+------------
+
+The hello packet is the first packet that the target sends to the host. If the
+host receives any other packet, it sends a reset command to the target. When the
+host receives a valid hello packet, it first verifies that the protocol running
+on the target is compatible with the protocol running on the host. If the
+protocol mismatch, the host sends a reset command to the target. The target uses
+the following format while sending a hello packet.
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+| Version | 4 | Version number of this protocol |
++-----------+-------------+--------------------------------------+
+| Version | 4 | Lowest Compatible version |
+| Compatible| | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Maximum command packet length |
+| packet | | (in bytes) the protocol supports. |
+| length | | |
++-----------+-------------+--------------------------------------+
+| Mode | 4 | Expected mode of target operation |
++-----------+-------------+--------------------------------------+
+| Reserved | 4 | Reserved for future use. |
++-----------+-------------+--------------------------------------+
+| Reserved | 4 | Reserved for future use. |
++-----------+-------------+--------------------------------------+
+| Reserved | 4 | Reserved for future use. |
++-----------+-------------+--------------------------------------+
+| Reserved | 4 | Reserved for future use. |
++-----------+-------------+--------------------------------------+
+| Reserved | 4 | Reserved for future use. |
++-----------+-------------+--------------------------------------+
+| Reserved | 4 | Reserved for future use. |
++-----------+-------------+--------------------------------------+
+
+The target also sends the following information:
+ 1. Maximum length of the command packet that it supports. The host uses this
+ information to avoid sending more bytes than the target can support in the
+ receiving command buffer.
+ 2. Mode of operation it expects to enter, based on the boot up sequence. The
+ supported modes of operation for the target are as follows:
+
++-----------------------------+---------+------------------------------------+
+| Mode | Mode ID | Description |
++-----------------------------+---------+------------------------------------+
+| SAHARA_MODE_IMAGE_TX_PENDING| 0x0 | Image transfer is in the pending |
+| | | mode. Transfer image from the host.|
+| | | After completion, the host should |
+| | | expect another image transfer |
+| | | request. |
++-----------------------------+---------+------------------------------------+
+|SAHARA_MODE_IMAGE_TX_COMPLETE| 0x1 | Image transfer is in the complete |
+| | | mode. Transfer image from the host.|
+| | | After completion, the host should |
+| | | not expect another image transfer |
+| | | request. |
++-----------------------------+---------+------------------------------------+
+| SAHARA_MODE_MEMORY_DBEUG | 0x2 | Memory debug mode. The host should |
+| | | prepare to receive a memory dump |
+| | | from the target. |
++-----------------------------+---------+------------------------------------+
+| SAHARA_MODE_COMMAND | 0x3 | Command mode. The host executes |
+| | | operations on the target by sending|
+| | | the appropriate client command to |
+| | | the Sahara client running on the |
+| | | target. The Sahar client interprets|
+| | | the client command and the response|
+| | | is sent after execution of the |
+| | | given command. |
++-----------------------------+---------+------------------------------------+
+
+Hello response packet
+---------------------
+
+After the host validates the protocol running on the target, it sends a response
+to the target. The response contains the following information.
+1. The protocol version that is running.
+2. The minimum protocol version that it supports.
+3. The mode of operation.
+
+The host sets the packet status field to success if no errors occur on the host
+side. After the target receives this packet, it can proceed with data transfer
+requests or memory debug. The host uses the following format while sending a
+hello response packet.
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++===========+=============+======================================+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet (in bytes) |
++-----------+-------------+--------------------------------------+
+| Version | 4 | Version number of this protocol |
++-----------+-------------+--------------------------------------+
+| Compatible| 4 | Lowest Compatible version |
++-----------+-------------+--------------------------------------+
+| Status | 4 | Success or error code |
++-----------+-------------+--------------------------------------+
+| Mode | 4 | Mode of operation for target to |
+| | | execute |
++-----------+-------------+--------------------------------------+
+| Reserved | 4 | Reserved for future use |
++-----------+-------------+--------------------------------------+
+| Reserved | 4 | Reserved for future use |
++-----------+-------------+--------------------------------------+
+| Reserved | 4 | Reserved for future use |
++-----------+-------------+--------------------------------------+
+| Reserved | 4 | Reserved for future use |
++-----------+-------------+--------------------------------------+
+| Reserved | 4 | Reserved for future use |
++-----------+-------------+--------------------------------------+
+| Reserved | 4 | Reserved for future use |
++-----------+-------------+--------------------------------------+
+
+
+Read data packet / 64 bit read data packet
+------------------------------------------
+
+The read data packet serves as a generic data transfer packet when any image
+data is to be transferred from the host to the target. This packet allows
+flexibility in the way that the image is transferred from the host to the
+target. As the target controls which data gets transferred, the target can
+determine what parts of the image get transferred and in what order. The host
+need not be familiar about the structure of the image. It must open the file and
+start transferring the data to the target based on the parameters specified in
+the packet.
+
+This gives the target complete control over how the images are transferred and
+processed. To initiate an image transfer, the target fills the read data packet
+with the image ID corresponding to the image that it wants to receive. The
+target also sends the offset into the image file and the length of the data(in
+bytes) it wants to read from the image. After the host receives this packet, the
+host responds with a data packet, which contains image data with the length
+specified in the read data packet. The host uses the following format while
+transferring the read data packet and 64-bit read data packet.
+
+
+Read data packet format
+=======================
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+| Image ID | 4 | ID of the image to be transferred. |
++-----------+-------------+--------------------------------------+
+| Data | 4 | Offset into the image file to start |
+| offset | | transferring data. |
++-----------+-------------+--------------------------------------+
+| Data | 4 | Number of bytes target wants to |
+| Length | | transfer from the image. |
++-----------+-------------+--------------------------------------+
+
+
+64-bit read data packet format
+==============================
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+| Image ID | 8 | ID of the image to be transferred. |
++-----------+-------------+--------------------------------------+
+| Data | 8 | Offset into the image file to start |
+| offset | | transferring data. |
++-----------+-------------+--------------------------------------+
+| Data | 8 | Number of bytes target wants to |
+| Length | | transfer from the image. |
++-----------+-------------+--------------------------------------+
+
+If any of the preceding fields are invalid, or if any other error occurs on the
+host, the host sends a data packet with length that does not match with what the
+target was expecting. The resulting error forces the target to send an end of
+image transfer packet with an error code in the status field and enables both
+the target and the host to enter an error handling state.
+
+End of Image transfer packet
+----------------------------
+
+If an image transfer is successfully completed, the target sends the host an end
+of image transfer packet with a success status. The target then waits for the
+host to send a done packet. If any error occurs during the transfer or
+processing of the image data, the status is set to the corresponding error code,
+and the target waits for a different command packet.
+
+The host uses the following format while transferring end of image transfer
+packet:
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+| Image ID | 4 | ID of the image that was being |
+| | | transferred. |
++-----------+-------------+--------------------------------------+
+| Status | 4 | Success or error code |
++-----------+-------------+--------------------------------------+
+
+Done packet
+-----------
+
+If the host receives an end of image transfer packet with a success status, the
+host sends a done packet to indicate the target that it can exit the protocol
+and continue execution of code. The host uses the following format while sending
+the done packet:
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+
+To transfer another image from the host, the target must re-initiate the
+protocol by starting with another hello packet.
+
+Done Response packet
+--------------------
+
+If the target receives a done packet, it responds with a done response packet
+containing the image transfer status. The target uses the following format while
+sending the done response packet:
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+| Image Tx | 4 | Indicates whether target is |
+| Status | | expecting to receive another image |
+| | | or not. |
++-----------+-------------+--------------------------------------+
+
+If all the images are transferred, the target sends a complete status to enable
+the host to exit the protocol. If all the images are not transferred, the target
+sends a pending status and waits for another hello packet to arrive.
+
+Reset Packet
+------------
+
+The host sends a reset packet to reset the target. The target services a reset
+request only if its in a state where reset requests are valid. If the target
+receives an invalid reset request, the target sends an error in an end of image
+transfer packet. The format of reset packet is as follows:
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+
+
+Reset response packet
+---------------------
+
+If the target receives a valid reset request, it sends a reset response packet
+just before it resets. The purpose of this response is to acknowledge the host
+that the target received the reset request. The format of reset response packet
+is as follows:
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+
+
+Memory debug packet
+-------------------
+
+The target initiates a memory dump by sending the host a memory debug packet.
+This packet contains the address and length of the memory debug table. The
+memory debug table is a listing of memory locations that can be accessed and
+dumped to the host. The target uses the following format while sending the
+memory debug packet:
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+| Memory | 4 | Target sets this field to the address|
+| table | | in memory that stores the memory |
+| Address | | debug table. |
++-----------+-------------+--------------------------------------+
+| Memory | 4 | Length in bytes of memory debug |
+| table | | table. |
+| Length | | |
++-----------+-------------+--------------------------------------+
+
+Given the memory table address and length, the host issues a memory read to
+retrieve the table. After the host receives the memory table information, it can
+decode each entry and issue memory read requests to dump each memory location.
+
+Memory read packet / 64-bit memory read packet
+----------------------------------------------
+
+The host issues memory read commands for each section of memory that it dumps.
+The host uses the following format while sending the memory read packet and 64
+bit memory read packet:
+
+Memory read packet format
+=========================
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+| Memory | 4 | Memory location to read. |
+| Address | | |
++-----------+-------------+--------------------------------------+
+| Memory | 4 | Length in bytes of memory to read |
+| Length | | |
++-----------+-------------+--------------------------------------+
+
+64 bit memory read packet format
+================================
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+| Memory | 8 | Memory location to read. |
+| Address | | |
++-----------+-------------+--------------------------------------+
+| Memory | 8 | Length in bytes of memory to read |
+| Length | | |
++-----------+-------------+--------------------------------------+
+
+The accessible regions are defined in the memory debug table. For each memory
+read command received, the target verifies that the specified memory(address and
+length) is accessible and responds with a raw data packet. The content and
+length of the raw data packet is the memory dump starting from the memory
+address and length specified in the memory read packet. The memory debug table
+can also be read using a memory read command by setting the address and length
+to the values specified in the memory debug packet.
+
+If any error occurs on the target, an end of image transfer packet is sent with
+the corresponding error code and the host recognizes whether it is actual memory
+data or an end of image transfer packet. The host issues a reset command on
+completion of a successful memory dump. However, the protocol does not force
+this implementation.
+
+Command ready packet
+--------------------
+
+The target sends this packet to the host to indicate that the target is ready to
+execute client commands. The target uses the following format while sending the
+command ready packet:
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+
+
+Command switch mode packet
+--------------------------
+
+The host sends the command switch mode packet to the target so that the target
+can switch to another mode. The host uses the following format while sending the
+command switch mode packet:
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+| Mode | 4 | Mode of operation for target |
+| | | to execute. |
++-----------+-------------+--------------------------------------+
+
+Command execute packet
+----------------------
+
+The host sends this packet to execute the given client command on the target. If
+the client command successfully executes, the target sends a command execute
+response packet. If an error occurs, the target sends an end of image transfer
+packet with the corresponding error code. The host uses the following format
+while sending command execute packet:
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+| Client | 4 | Client Command to be executed. |
+| Command | | |
++-----------+-------------+--------------------------------------+
+
+
+Client commands
+===============
+
++------------+-------------+--------------------------------------+
+| Client ID | Length | Description |
++------------+-------------+--------------------------------------+
+| 0x8 | 4 | Get Command ID list. |
++------------+-------------+--------------------------------------+
+| 0x9 | 4 | Get DDR training data. |
++------------+-------------+--------------------------------------+
+
+Command execute Response packet
+-------------------------------
+
+The target sends this packet if it successfully executes the client command. The
+target uses the following format while sending the command execute response
+packet.
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+| Client | 4 | Client Command to be executed. |
+| Command | | |
++-----------+-------------+--------------------------------------+
+| Response | 4 | Number of bytes for response data. |
+| Length | | |
++-----------+-------------+--------------------------------------+
+
+Command execute data packet
+---------------------------
+
+The host sends this packet if the response length received in the command
+execute response packet is greater than 0. The host uses the following format
+while sending command execute data packet:
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+| Client | 4 | Client Command executed. |
+| Command | | |
++-----------+-------------+--------------------------------------+
+
+The packet indicates the target to send the response data in a raw data packet.
+The target sends the response data upon receiving this packet.
+
+64-bit memory debug packet
+--------------------------
+
+The target sends this packet to the host to initiate a memory dump. The packet
+contains 64-bit address and length of the memory table. The target uses the
+following format while sending 64-bit memory debug packet:
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+| Memory | 8 | Target sets this field to the 64-bit |
+| table | | address in memory that stores the |
+| Address | | memory debug table. |
++-----------+-------------+--------------------------------------+
+| Memory | 8 | Length in bytes of memory debug |
+| table | | table. |
+| Length | | |
++-----------+-------------+--------------------------------------+
+
+Reset Sahara state machine packet
+---------------------------------
+
+The host sends a reset Sahara state machine packet whenever it wants to reset
+Sahara state machine. When the target receives a reset Sahara state machine
+request, it reinitializes Sahara protocol and sends the hello packet to the
+host. The Sahara protocol is restarted without a target reset. The host uses the
+following format while sending the reset Sahara state machine packet:
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+
+Write data packet
+-----------------
+
+Write data packet serves as a generic data transfer packet when any data is
+transferred from the target to the host. This packet allows flexible data
+transfer from the target to the host.
+
+As the target controls what data gets transferred, target can determine what
+parts of the data get transferred and in what order. The host does not need to
+know anything about the structure of the data. It only needs to open the file
+and start accepting the data to the host based on the parameters specified in
+the packet.
+
+To initiate a write data transfer, the target fills the write data packet with
+the image ID corresponding to the image data that it wants to send. The target
+also sends the offset into the output file and the length of the data(in bytes)
+it wants to write from the target. As soon as the host receives the packet, the
+host opens an output file and waits to receive the data packets. After the
+packet is received, the content from the data pcket is written to the output
+file, The format of the write data packet is as follows:
+
++-----------+-------------+--------------------------------------+
+| Field | Length | Description |
+| | (bytes) | |
++-----------+-------------+--------------------------------------+
+| Command | 4 | Command identifier code |
++-----------+-------------+--------------------------------------+
+| Length | 4 | Length of the packet(in bytes) |
++-----------+-------------+--------------------------------------+
+| Data | 8 | Offset into the image file to start |
+| offset | | writing the data to host. |
++-----------+-------------+--------------------------------------+
+| Image ID | 4 | ID of the image to be transferred. |
++-----------+-------------+--------------------------------------+
+| Data | 4 | Number of bytes target wants to |
+| Length | | transfer the data to the host. |
++-----------+-------------+--------------------------------------+
+
+
+Command packet flow between host and target
+-------------------------------------------
+
+Packet flow is a process of exchange of information as packets between the host
+and the target in a specific way using command packets. The Sahara protocol
+allows packet processing for the following scenarios:
+
+1. Transferring an image from the host to the target.
+2. Dumping memory from the target to the host.
+3. Loading DDR calibration data on flashless target.
+
+Packet flow for Image transfer
+------------------------------
+
+The packet flow is performed between the host and target for a successful image
+transfer.
+
+.. code-block:: text
+
+ Host Target
+ | HELLO |
+ | (mode = image transfer) |
+ |<--------------------------|
+ | |
+ | HELLO RESP |
+ | (mode = image transfer) |
+ |-------------------------->|
+ | |
+ | READ_DATA |
+ | (img ID, 0, offset, |
+ | size of image header) |
+ |<--------------------------|
+ | |
+ | RAW_DATA |
+ | (size of image header) |
+ |-------------------------->|
+ | |
+ | READ_DATA |
+ | (img ID, segment 0 offset,|
+ | size of segment 0) |
+ |<--------------------------|
+ | RAW_DATA |
+ | (size of segment 0) |
+ |-------------------------->|
+ | |
+ | READ_DATA |
+ | (img ID, segment 1 offset,|
+ | size of segment 1) |
+ |<--------------------------|
+ | |
+ | |
+ | RAW_DATA |
+ | (size of segment 1) |
+ |-------------------------->|
+ | ... |
+ | ... |
+ | ... |
+ | ... |
+ | |
+ | |
+ | READ_DATA |
+ | (img ID, segment N offset,|
+ | size of segment N) |
+ |<--------------------------|
+ | |
+ | |
+ | |
+ | RAW_DATA |
+ | (size of segment N) |
+ |-------------------------->|
+ | |
+ | |
+ | END_IMAGE_TX |
+ |<--------------------------|
+ | |
+ | |
+ | DONE |
+ |-------------------------->|
+ | |
+ | |
+ | DONE_RESP |
+ |<--------------------------|
+ | |
+
+The packet flow sequence for image transfer is as follows:
+
+1. A hello packet is sent from the target to the host to initiate the protocol
+ with the mode set to either image transfer pending or image transfer
+ complete (depending on the target's boot sequence).
+
+2. The host sends a hello response packet with a success status and sets the
+ mode to the mode received in the hello packet. After it receives the hello
+ packet and validates the protocol version running on the target.
+
+3. After the target receives the hello response, the target initiates the
+ image transfer request by sending read data packets. Each read data packet
+ specifies the image that the target wishes to receive and what part of the
+ image is to be transferred.
+
+4. During normal operation, the target first requests image header information.
+
+ a. The image header information specifies image size and location of the
+ image data that is to be transferred.
+
+ b. The image header information (which is sent as a read data request)
+ allows the target to know the format of the image to be transferred.
+ The protocol does not require the host to know anything about the
+ image formats and allows the host to read and transfer data from the
+ image as requested by the target.
+
+ c. If the image is a standalone binary image without any data segmentation
+ (which means the data is entirely contiguous when stored as well as
+ transferred to the target system memory), then the target requests for
+ entire image data to be sent in one transfer.
+
+ d. If the image data is segmented and requires scattering of the data
+ segments to noncontiguous system memory locations, the target issues
+ multiple read data requests to enable each data segment to be
+ transferred directly to the respective destination address. This
+ scattered information resides in the image header and is parsed by the
+ target before issuing the read data requests.
+
+5. After receiving a read data request, the host parses the image ID, data
+ offset, and data length to transfer data from the corresponding image file.
+ The host sends the requested data without any packet header.
+
+6. The target directly transfers the data to the destination address without
+ any software processing or temporarily buffering of the data in system
+ memory by transferring the image header to the targert and setting the
+ receive buffer for the data as the destination address in system memory.
+
+7. After the target successfully receives all segments for an image, the
+ target sends an end of image transfer packet with the image ID of the
+ corresponding image, and a success status. The host stops reading and
+ closes the image file after receiving the success status.
+
+8. The host sends a done packet to allow the target to exit the protocol after
+ it receives a successgul end of image transfer packet.
+
+9. After the target receives the done packet, the target sends a done response
+ packet to the host. This packet indicates if the target expects another
+ image to be transferred and if the host can continue to run the protocol.
+
+Packet flow for memory debug
+----------------------------
+
+The packet flow is performed between the host and the target for the successful
+memory debug.
+
+.. code-block:: text
+
+ Host Target
+ | HELLO |
+ | (mode = memory debug) |
+ |<--------------------------|
+ | |
+ | HELLO RESP |
+ | (mode = memory debug) |
+ |-------------------------->|
+ | |
+ | MEMORY_DEBUG |
+ | (location of mem table, |
+ | size of memory table) |
+ |<--------------------------|
+ | |
+ | MEMORY_READ |
+ | (Address from region 0 ,|
+ | size of region 0) |
+ |-------------------------->|
+ | RAW_DATA |
+ | (size of region 0) |
+ |<--------------------------|
+ | |
+ | MEMORY_READ |
+ | (Address from region 1 ,|
+ | size of region 1) |
+ |-------------------------->|
+ | RAW_DATA |
+ | (size of region 1) |
+ |<--------------------------|
+ | MEMORY_READ |
+ | (Address from region 2 ,|
+ | size of region 0) |
+ |-------------------------->|
+ | RAW_DATA |
+ | (size of region 2) |
+ |<--------------------------|
+ | ... |
+ | ... |
+ | ... |
+ | ... |
+ | |
+ | MEMORY_READ |
+ | (Address from region N ,|
+ | size of region N) |
+ |-------------------------->|
+ | RAW_DATA |
+ | (size of region N) |
+ |<--------------------------|
+ | |
+ | RESET |
+ |-------------------------->|
+ | |
+ | |
+ | RESET_RESP |
+ |<--------------------------|
+ | |
+
+The packet flow sequence for image transfer is as follows:
+
+1. A hello packet is sent from the target to the host to initiate the protocol
+ with mode set to memory debug.
+
+2. The host sends a hello response packet with a success status and sets the
+ mode to memory debug after it receives the hello packet and validates the
+ protocol version running on the target.
+
+3. After the target receives the hello response, the target initiates the
+ memory dump by sending a memory debug packet with the location and size of
+ the memory debug table. The memory debug table specifies accessible memory
+ regions.
+
+4. The host then initiates a memory read packet to read the memory debug
+ table and receives the table in a raw data packet after it receives the
+ memory debug packet.
+
+5. The host then decodes the table and issues memory reads for each accessible
+ region. The data for each region is sent in a raw data packet.
+
+6. Upon completion, the host issues a reset to the target. The target sends a
+ reset response and resets the target.
+
+7. The host can alternatively send a command switch mode packet to allow the
+ target to switch modes and avoid a reset.
+
+
+Packet flow to load DDR calibration data on target
+--------------------------------------------------
+
+The packet flow is performed between the host and the target to load DDR
+calibration data on flashless target. This packet flow is initiated when the
+device boots up for the first time and needs DDR calibration. This packet flow
+is also initiated in other scenarios, such as build update or any reason for
+which DDR calibration data gets corrupted.
+
+First boot scenario or invalid calibration data in filesystem.
+--------------------------------------------------------------
+
+.. code-block:: text
+
+ Host Target
+ | HELLO |
+ | (mode = image transfer) |
+ |<--------------------------|
+ | |
+ | HELLO RESP |
+ | (mode = image transfer) |
+ |-------------------------->|
+ | |
+ | READ_DATA |
+ | (img ID:34, 0, offset, |
+ | size of DDR training data)|
+ |<--------------------------|
+ | |
+ | RAW_DATA |
+ |(size of DDR training data)|
+ |-------------------------->|
+ | |
+ | |
+ | END_IMAGE_TX |
+ |<--------------------------|
+ | |
+ | |
+ | DONE |
+ |-------------------------->|
+ | |
+ | |
+ | DONE_RESP |
+ | (mode = IMAGE_TX_PENDING) |
+ |<--------------------------|
+ |1. First boot scenario. |
+ | DDR driver performs |
+ | calibration and returns |
+ | to SBL. |
+ |2. Next: Push DDR |
+ | Calibration data to host |
+ | |
+ | |
+ | HELLO |
+ | (mode = COMMAND mode) |
+ |<--------------------------|
+ | |
+ | HELLO RESP |
+ | (mode = COMMAND mode ) |
+ |-------------------------->|
+ | |
+ | CMD_READY |
+ |<--------------------------|
+ | |
+ | CMD_EXEC |
+ |(cmd id = 8, Get command |
+ | ID to be executed) |
+ |-------------------------->|
+ | |
+ | CMD_EXEC_RESP |
+ |(cmd id: 8, resp len = 4) |
+ |<--------------------------|
+ | |
+ | CMD_EXEC_GET_DATA |
+ | (ID = 0x8) |
+ |-------------------------->|
+ | |
+ | RAW_DATA |
+ | (0x00000009) |
+ |<--------------------------|
+ | |
+ | CMD_EXEC |
+ | (cmd id: 9, resp len > 0) |
+ |-------------------------->|
+ | |
+ | |
+ | CMD_EXEC_RESP |
+ |(cmd id: 9, resp len > 0) |
+ |<--------------------------|
+ | |
+ | CMD_EXEC_GET_DATA |
+ | (ID = 0x9) |
+ |-------------------------->|
+ | |
+ | RAW_DATA |
+ | (valid training data) |
+ |<--------------------------|
+ | |
+ |3. Host sends switch to |
+ |image tx mode to continue |
+ |booting. |
+ | |
+ | |
+ | CMD_SWITCH_MODE |
+ | (mode = IMAGE_TX_PENDING) |
+ |-------------------------->|
+ | |
+ | |
+ | HELLO |
+ | (mode = IMAGE_TX_PENDING) |
+ |<--------------------------|
+ | |
+ | HELLO RESP |
+ | (mode = IMAGE_TX_PENDING) |
+ |-------------------------->|
+ | |
+ |4. Boot/Load rest of the |
+ | images.... |
+ | |
+ | END_IMAGE_TX |
+ |<--------------------------|
+ | |
+ | |
+ | DONE |
+ |-------------------------->|
+ | |
+ | |
+ | DONE_RESP |
+ |(mode = IMAGE_TX_COMPLETE) |
+ |<--------------------------|
+ | |
+
+The packet flow sequence is as follows:
+
+1. The target sends the hello packet to the host to initiate the protocol
+ with the mode set to image transfer pending.
+
+2. The host sends a hello response packet with a success status and sets the
+ mode to image transfer pending after it receives the hello packet and
+ validates the protocol version running on the target.
+
+3. After the target receives the hello response, it initiates the data
+ transfer by requesting the size of DDR training/calibration data.
+
+4. The host sends back the DDR training/calibration data to the target.
+
+5. The target decodes the training data and does not find valid DDR
+ calibration data, target sends END_IMAGE_TX to interrupt the transfer.
+
+6. The host sends DONE after receives END_IMAGE_TX.
+
+7. The target sends DONE_RESP with mode = IMAGE_TX_PENDING because it has
+ not received all images.
+
+8. The target executes DDR training process to generate valid DDR calibration
+ data and prepares to push back to host.
+
+9. The target initiates protocol by sending a hello packet with COMMAND_MODE
+ to the host.
+
+10. The host sends a hello response packet with a success status and sets the
+ mode to COMMAND_MODE.
+
+11. The target sends CMD_READY to the host.
+
+12. The host receives CMD_READY and starts to get command IDs to be executed.
+
+13. The target sends CMD_ID = 9 to push DDR calibration data to host.
+
+14. The host executes CMD_ID = 9 to get DDR calibration data from the target.
+
+15. The target sends RAW_DATA with the payload which contains DDR calibration
+ data to host.
+
+16. The host saves training data in the kernel buffer and exposes to userspace
+ via the sysfs entry. The host sends CMD_SWITCH_MODE with the mode set to
+ IMAGE_TX_PENDING to continue booting.
+
+17. After the target receives the CMD_SWITCH_MODE command, it sends HELLO to
+ the host with the mode set to IMAGE_TX_PENDING. The target and the host
+ repeat the packet flow for image transfer to get all booting-required
+ images.
+
+18. Upon successful transfer of all images, the target sends an END_IMAGE_TX
+ packet with a success status to the host.
+
+19. The host sends DONE after it receives END_IMAGE_TX.
+
+20. The target sends DONE_RESP with the mode set to IMAGE_TX_COMPLETE because
+ it has received all images. The process has been completed after the host
+ receives DONE_RESP with the mode set to IMAGE_TX_COMPLETE.
+
+Subsequent boot scenario with valid DDR calibration data
+--------------------------------------------------------
+
+The below firgure shows the subsequent boot scenario with valid DDR calibration
+data process being loaded from host to target.
+
+.. code-block:: text
+
+ Host Target
+ | HELLO |
+ | (mode = image transfer) |
+ |<--------------------------|
+ | |
+ | HELLO RESP |
+ | (mode = image transfer) |
+ |-------------------------->|
+ | |
+ | READ_DATA |
+ | (img ID:34, 0, offset, |
+ | size of DDR training data)|
+ |<--------------------------|
+ | |
+ | RAW_DATA |
+ |(size of DDR training data)|
+ |-------------------------->|
+ | |
+ | |
+ | END_IMAGE_TX |
+ |<--------------------------|
+ | |
+ | |
+ | DONE |
+ |-------------------------->|
+ | |
+ | |
+ | DONE_RESP |
+ | (mode = IMAGE_TX_PENDING) |
+ |<--------------------------|
+ | |
+ | Subsequent boot scenario |
+ | (valid calibration data) |
+ | DDR driver configures DDR |
+ | using valid calibration |
+ | data |
+ | |
+ | |
+ | HELLO |
+ | (mode = IMAGE_TX_PENDING) |
+ |<--------------------------|
+ | |
+ | HELLO RESP |
+ | (mode = IMAGE_TX_PENDING) |
+ |-------------------------->|
+ | |
+ | Boot/Load rest of the |
+ | images.... |
+ | |
+ | END_IMAGE_TX |
+ |<--------------------------|
+ | |
+ | |
+ | DONE |
+ |-------------------------->|
+ | |
+ | |
+ | DONE_RESP |
+ |(mode = IMAGE_TX_COMPLETE) |
+ |<--------------------------|
+ | |
+
+The packet flow is as follows:
+
+1. The target sends the hello packet to the host to initiate the protocol
+ with the mode set to image transfer pending.
+
+2. The host sends a hello response packet with a success status and sets the
+ mode to image transfer pending after it receives the hello packet and
+ validates the protocol version running on the target.
+
+3. After the target receives the hello response, it initiates the images
+ transfer by requesting the training/calibration data from the host.
+
+4. The host sends back the DDR training/calibration data to the target.
+
+5. The target decodes the DDR training/calibration data and finds valid DDR
+ calibration data.
+
+6. The host sends RAW_DATA with the size of the DDR calibration data to the
+ target.
+
+7. Upon successful transfer of DDR calibration data, the target sends an
+ END_IMAGE_TX packet with a success status.
+
+8. The host sends DONE after it receives END_IMAGE_TX.
+
+9. The target sends DONE_RESP with mode = IMAGE_TX_PENDING because it has not
+ received all images.
+
+10. The target continues booting with valid DDR calibration data.
+
+11. The target and the host repeat the packet flow for image transfer to get
+ all booting-required images.
+
+12. After successful transfer of all images, the target sends an END_IMAGE_TX
+ packet with a success status to the host.
+
+13. The host sends DONE after it receives END_IMAGE_TX.
+
+14. The target sends DONE_RESP with the mode set to IMAGE_TX_COMPLETE because
+ it has received all images. The process has been completed after the host
+ receives DONE_RESP with the mode set to IMAGE_TX_COMPLETE.
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v9 2/7] bus: mhi: Move Sahara protocol driver under MHI host client drivers
2026-10-13 8:45 [PATCH v9 0/7] Qualcomm Sahara protocol enhancements Kishore Batta
2026-10-13 8:45 ` [PATCH v9 1/7] Add documentation for Sahara protocol Kishore Batta
@ 2026-10-13 8:45 ` Kishore Batta
2026-10-13 9:00 ` sashiko-bot
2026-10-13 8:45 ` [PATCH v9 3/7] bus: mhi: Centralize Sahara firmware image table selection at probe time Kishore Batta
` (4 subsequent siblings)
6 siblings, 1 reply; 14+ messages in thread
From: Kishore Batta @ 2026-10-13 8:45 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Jeff Hugo, Carl Vanderlip,
Oded Gabbay, Manivannan Sadhasivam, Randy Dunlap
Cc: linux-doc, linux-kernel, linux-arm-msm, dri-devel, mhi,
Kishore Batta
The Sahara protocol driver currently lives under the QAIC accelerator
subsystem even though the protocol is transported over MHI and is used by
multiple Qualcomm flashless devices. This makes Sahara appear QAIC specific
and complicates reuse by other MHI based devices.
Move the Sahara protocol driver under drivers/bus/mhi as a host client
driver and build it as an independent MHI protocol driver. This keeps the
QAIC driver focused on the accelerator device while allowing other MHI
users to enable Sahara without depending on QAIC.
As part of the move, add a dedicated Kconfig/Makefile hierarchy under the
MHI host client drivers and convert the driver to use module_mhi_driver()
instead of register/unregister hooks.
Signed-off-by: Kishore Batta <kishore.batta@oss.qualcomm.com>
---
drivers/accel/qaic/Kconfig | 1 +
drivers/accel/qaic/Makefile | 3 +--
drivers/accel/qaic/qaic_drv.c | 9 ---------
drivers/accel/qaic/sahara.h | 10 ----------
drivers/bus/mhi/host/Makefile | 2 +-
drivers/bus/mhi/host/clients/Kconfig | 6 ++++++
drivers/bus/mhi/host/clients/Makefile | 1 +
drivers/bus/mhi/host/clients/sahara/Kconfig | 15 +++++++++++++++
drivers/bus/mhi/host/clients/sahara/Makefile | 2 ++
.../qaic => bus/mhi/host/clients/sahara}/sahara.c | 20 +++++++-------------
10 files changed, 34 insertions(+), 35 deletions(-)
diff --git a/drivers/accel/qaic/Kconfig b/drivers/accel/qaic/Kconfig
index 116e42d152ca885b8c59e33c7a87519a0abc6bb3..af90fdfcf77eeb6dd5ad309b33d793d4fdc91b1e 100644
--- a/drivers/accel/qaic/Kconfig
+++ b/drivers/accel/qaic/Kconfig
@@ -8,6 +8,7 @@ config DRM_ACCEL_QAIC
depends on DRM_ACCEL
depends on PCI && HAS_IOMEM
depends on MHI_BUS
+ depends on MHI_SAHARA
select CRC32
select WANT_DEV_COREDUMP
help
diff --git a/drivers/accel/qaic/Makefile b/drivers/accel/qaic/Makefile
index 71f727b74da3bb4478324689f02a7cea24a05c2d..e7b8458800072aa627f7f36c3257883aa56f4ce4 100644
--- a/drivers/accel/qaic/Makefile
+++ b/drivers/accel/qaic/Makefile
@@ -13,7 +13,6 @@ qaic-y := \
qaic_ras.o \
qaic_ssr.o \
qaic_sysfs.o \
- qaic_timesync.o \
- sahara.o
+ qaic_timesync.o
qaic-$(CONFIG_DEBUG_FS) += qaic_debugfs.o
diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c
index 0acf6cdd44f81430ec52c7120b8b50c60721a615..64e87dfaa560376ecdf1e6f8bf7abf6153160926 100644
--- a/drivers/accel/qaic/qaic_drv.c
+++ b/drivers/accel/qaic/qaic_drv.c
@@ -32,7 +32,6 @@
#include "qaic_ras.h"
#include "qaic_ssr.h"
#include "qaic_timesync.h"
-#include "sahara.h"
MODULE_IMPORT_NS("DMA_BUF");
@@ -789,12 +788,6 @@ static int __init qaic_init(void)
goto free_pci;
}
- ret = sahara_register();
- if (ret) {
- pr_debug("qaic: sahara_register failed %d\n", ret);
- goto free_mhi;
- }
-
ret = qaic_timesync_init();
if (ret)
pr_debug("qaic: qaic_timesync_init failed %d\n", ret);
@@ -816,7 +809,6 @@ static int __init qaic_init(void)
free_bootlog:
qaic_bootlog_unregister();
-free_mhi:
mhi_driver_unregister(&qaic_mhi_driver);
free_pci:
pci_unregister_driver(&qaic_pci_driver);
@@ -845,7 +837,6 @@ static void __exit qaic_exit(void)
qaic_ras_unregister();
qaic_bootlog_unregister();
qaic_timesync_deinit();
- sahara_unregister();
mhi_driver_unregister(&qaic_mhi_driver);
pci_unregister_driver(&qaic_pci_driver);
}
diff --git a/drivers/accel/qaic/sahara.h b/drivers/accel/qaic/sahara.h
deleted file mode 100644
index 08037281c80e28f6c85b924c45ce2c2f9dc60c39..0000000000000000000000000000000000000000
--- a/drivers/accel/qaic/sahara.h
+++ /dev/null
@@ -1,10 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-
-/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
-
-#ifndef __SAHARA_H__
-#define __SAHARA_H__
-
-int sahara_register(void);
-void sahara_unregister(void);
-#endif /* __SAHARA_H__ */
diff --git a/drivers/bus/mhi/host/Makefile b/drivers/bus/mhi/host/Makefile
index 2a16008aeb38127494782bbff4e1656428d2b776..2e8949f1a2fe6f3f3b2e1dc541f97d2c393d6a0f 100644
--- a/drivers/bus/mhi/host/Makefile
+++ b/drivers/bus/mhi/host/Makefile
@@ -4,4 +4,4 @@ mhi-$(CONFIG_MHI_BUS_DEBUG) += debugfs.o
obj-$(CONFIG_MHI_BUS_PCI_GENERIC) += mhi_pci_generic.o
mhi_pci_generic-y += pci_generic.o
-obj-y += clients/
+obj-$(CONFIG_MHI_BUS) += clients/
diff --git a/drivers/bus/mhi/host/clients/Kconfig b/drivers/bus/mhi/host/clients/Kconfig
index 8bb5715e61d0d6e64a51012b4e1594ba46e5bfc1..1932344157a5b1dc25c8d63f6d4ac38f896d08a7 100644
--- a/drivers/bus/mhi/host/clients/Kconfig
+++ b/drivers/bus/mhi/host/clients/Kconfig
@@ -15,3 +15,9 @@ config MHI_BUS_LOOPBACK
To compile this driver as a module, choose M here. The module
will be called mhi_loopback.
+
+menu "MHI host client drivers"
+
+source "drivers/bus/mhi/host/clients/sahara/Kconfig"
+
+endmenu
diff --git a/drivers/bus/mhi/host/clients/Makefile b/drivers/bus/mhi/host/clients/Makefile
index 3811b6928f42b38f94b1167941cf3b0fe512d32b..67e70de280369870164b4808f39d9b3340f2589b 100644
--- a/drivers/bus/mhi/host/clients/Makefile
+++ b/drivers/bus/mhi/host/clients/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_MHI_BUS_LOOPBACK) += mhi_loopback.o
mhi_loopback-y += loopback.o
+obj-$(CONFIG_MHI_SAHARA) += sahara/
diff --git a/drivers/bus/mhi/host/clients/sahara/Kconfig b/drivers/bus/mhi/host/clients/sahara/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..f1fc7013a2dee0be645c50f32305659e591de7e7
--- /dev/null
+++ b/drivers/bus/mhi/host/clients/sahara/Kconfig
@@ -0,0 +1,15 @@
+config MHI_SAHARA
+ tristate "Sahara protocol driver"
+ depends on MHI_BUS
+ help
+ Enable support for the Sahara protocol transported over the MHI bus.
+
+ The Sahara protocol is used to transfer firmware images, retrieve
+ memory dumps and exchange command mode DDR calibration data between
+ host and device. This driver is not tied to a specific SoC and may be
+ used by multiple MHI based devices.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the module will be
+ called mhi_sahara.
diff --git a/drivers/bus/mhi/host/clients/sahara/Makefile b/drivers/bus/mhi/host/clients/sahara/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..fc02a25935011cbd7138ea8f24b88cf5b032a4ce
--- /dev/null
+++ b/drivers/bus/mhi/host/clients/sahara/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_MHI_SAHARA) += mhi_sahara.o
+mhi_sahara-y := sahara.o
diff --git a/drivers/accel/qaic/sahara.c b/drivers/bus/mhi/host/clients/sahara/sahara.c
similarity index 99%
rename from drivers/accel/qaic/sahara.c
rename to drivers/bus/mhi/host/clients/sahara/sahara.c
index c7c0b3eb4b65fb7776a6c724cb3105046cf6f7eb..75c0cf6010e1506fe661cfd110112bbb37465880 100644
--- a/drivers/accel/qaic/sahara.c
+++ b/drivers/bus/mhi/host/clients/sahara/sahara.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
-
-/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ */
#include <linux/devcoredump.h>
#include <linux/firmware.h>
@@ -12,8 +14,6 @@
#include <linux/vmalloc.h>
#include <linux/workqueue.h>
-#include "sahara.h"
-
#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 */
@@ -922,13 +922,7 @@ static struct mhi_driver sahara_mhi_driver = {
.name = "sahara",
},
};
+module_mhi_driver(sahara_mhi_driver);
-int sahara_register(void)
-{
- return mhi_driver_register(&sahara_mhi_driver);
-}
-
-void sahara_unregister(void)
-{
- mhi_driver_unregister(&sahara_mhi_driver);
-}
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Qualcomm Sahara MHI protocol driver");
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v9 3/7] bus: mhi: Centralize Sahara firmware image table selection at probe time
2026-10-13 8:45 [PATCH v9 0/7] Qualcomm Sahara protocol enhancements Kishore Batta
2026-10-13 8:45 ` [PATCH v9 1/7] Add documentation for Sahara protocol Kishore Batta
2026-10-13 8:45 ` [PATCH v9 2/7] bus: mhi: Move Sahara protocol driver under MHI host client drivers Kishore Batta
@ 2026-10-13 8:45 ` Kishore Batta
2026-10-13 8:45 ` [PATCH v9 4/7] bus: mhi: Add QDU100 Sahara variant and firmware fallback Kishore Batta
` (3 subsequent siblings)
6 siblings, 0 replies; 14+ messages in thread
From: Kishore Batta @ 2026-10-13 8:45 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Jeff Hugo, Carl Vanderlip,
Oded Gabbay, Manivannan Sadhasivam, Randy Dunlap
Cc: linux-doc, linux-kernel, linux-arm-msm, dri-devel, mhi,
Kishore Batta
The Sahara driver currently selects firmware image tables using scattered,
device specific conditionals in the probe path. This makes the logic harder
to follow, harder to extend for new devices, and spreads device knowledge
across multiple code paths.
Refactor firmware image table selection into a single, explicit probe time
mechanism by introducing a controller provided firmware mapping table that
captures device matching, Sahara image tables, firmware folder names, and
streaming behaviour in one place.
This centralizes device specific decisions in the controller driver,
simplifies the Sahara probe logic, and removes ad-hoc conditionals while
preserving existing behavior for all supported AIC devices. This is in
preparation for adding QDU100 support.
Signed-off-by: Kishore Batta <kishore.batta@oss.qualcomm.com>
---
drivers/accel/qaic/mhi_controller.c | 61 ++++++++++++++++++++++++++++
drivers/bus/mhi/host/clients/sahara/sahara.c | 60 +++++----------------------
include/linux/mhi.h | 17 ++++++++
3 files changed, 88 insertions(+), 50 deletions(-)
diff --git a/drivers/accel/qaic/mhi_controller.c b/drivers/accel/qaic/mhi_controller.c
index 40e6d262ef217aad8ddff6f69a38388ef683e3ed..dd320739c07ad4a35b74446e32f0e8e64e847d16 100644
--- a/drivers/accel/qaic/mhi_controller.c
+++ b/drivers/accel/qaic/mhi_controller.c
@@ -20,6 +20,62 @@ static unsigned int mhi_timeout_ms = 2000; /* 2 sec default */
module_param(mhi_timeout_ms, uint, 0600);
MODULE_PARM_DESC(mhi_timeout_ms, "MHI controller timeout value");
+static const char * const aic100_image_table[] = {
+ [1] = "qcom/aic100/fw1.bin",
+ [2] = "qcom/aic100/fw2.bin",
+ [4] = "qcom/aic100/fw4.bin",
+ [5] = "qcom/aic100/fw5.bin",
+ [6] = "qcom/aic100/fw6.bin",
+ [8] = "qcom/aic100/fw8.bin",
+ [9] = "qcom/aic100/fw9.bin",
+ [10] = "qcom/aic100/fw10.bin",
+};
+
+static const char * const aic200_image_table[] = {
+ [5] = "qcom/aic200/uefi.elf",
+ [12] = "qcom/aic200/aic200-nsp.bin",
+ [23] = "qcom/aic200/aop.mbn",
+ [32] = "qcom/aic200/tz.mbn",
+ [33] = "qcom/aic200/hypvm.mbn",
+ [38] = "qcom/aic200/xbl_config.elf",
+ [39] = "qcom/aic200/aic200_abl.elf",
+ [40] = "qcom/aic200/apdp.mbn",
+ [41] = "qcom/aic200/devcfg.mbn",
+ [42] = "qcom/aic200/sec.elf",
+ [43] = "qcom/aic200/aic200-hlos.elf",
+ [49] = "qcom/aic200/shrm.elf",
+ [50] = "qcom/aic200/cpucp.elf",
+ [51] = "qcom/aic200/aop_devcfg.mbn",
+ [54] = "qcom/aic200/qupv3fw.elf",
+ [57] = "qcom/aic200/cpucp_dtbs.elf",
+ [62] = "qcom/aic200/uefi_dtbs.elf",
+ [63] = "qcom/aic200/xbl_ac_config.mbn",
+ [64] = "qcom/aic200/tz_ac_config.mbn",
+ [65] = "qcom/aic200/hyp_ac_config.mbn",
+ [66] = "qcom/aic200/pdp.elf",
+ [67] = "qcom/aic200/pdp_cdb.elf",
+ [68] = "qcom/aic200/sdi.mbn",
+ [69] = "qcom/aic200/dcd.mbn",
+ [73] = "qcom/aic200/gearvm.mbn",
+ [74] = "qcom/aic200/sti.bin",
+ [76] = "qcom/aic200/tz_qti_config.mbn",
+ [78] = "qcom/aic200/pvs.bin",
+};
+
+static const struct mhi_sahara_fw_table aic100_sahara_fw = {
+ .image_table = aic100_image_table,
+ .table_size = ARRAY_SIZE(aic100_image_table),
+ .fw_folder = "aic100",
+ .non_streaming = true,
+};
+
+static const struct mhi_sahara_fw_table aic200_sahara_fw = {
+ .image_table = aic200_image_table,
+ .table_size = ARRAY_SIZE(aic200_image_table),
+ .fw_folder = "aic200",
+ .non_streaming = false,
+};
+
static const char *fw_image_paths[FAMILY_MAX] = {
[FAMILY_AIC100] = "qcom/aic100/sbl.bin",
[FAMILY_AIC200] = "qcom/aic200/sbl.bin",
@@ -871,6 +927,11 @@ struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, voi
mhi_cntrl->name = "AIC100";
}
+ if (mhi_cntrl->name && !strcmp(mhi_cntrl->name, "AIC100"))
+ mhi_cntrl->sahara_fw = &aic100_sahara_fw;
+ else if (mhi_cntrl->name && !strcmp(mhi_cntrl->name, "AIC200"))
+ mhi_cntrl->sahara_fw = &aic200_sahara_fw;
+
/* use latest configured timeout */
mhi_config.timeout_ms = mhi_timeout_ms;
ret = mhi_register_controller(mhi_cntrl, &mhi_config);
diff --git a/drivers/bus/mhi/host/clients/sahara/sahara.c b/drivers/bus/mhi/host/clients/sahara/sahara.c
index 75c0cf6010e1506fe661cfd110112bbb37465880..ce4fcbd40baa1b3a3d65d0d10fc9e54b1c7eb04d 100644
--- a/drivers/bus/mhi/host/clients/sahara/sahara.c
+++ b/drivers/bus/mhi/host/clients/sahara/sahara.c
@@ -178,48 +178,7 @@ struct sahara_context {
u32 read_data_length;
bool is_mem_dump_mode;
bool non_streaming;
-};
-
-static const char * const aic100_image_table[] = {
- [1] = "qcom/aic100/fw1.bin",
- [2] = "qcom/aic100/fw2.bin",
- [4] = "qcom/aic100/fw4.bin",
- [5] = "qcom/aic100/fw5.bin",
- [6] = "qcom/aic100/fw6.bin",
- [8] = "qcom/aic100/fw8.bin",
- [9] = "qcom/aic100/fw9.bin",
- [10] = "qcom/aic100/fw10.bin",
-};
-
-static const char * const aic200_image_table[] = {
- [5] = "qcom/aic200/uefi.elf",
- [12] = "qcom/aic200/aic200-nsp.bin",
- [23] = "qcom/aic200/aop.mbn",
- [32] = "qcom/aic200/tz.mbn",
- [33] = "qcom/aic200/hypvm.mbn",
- [38] = "qcom/aic200/xbl_config.elf",
- [39] = "qcom/aic200/aic200_abl.elf",
- [40] = "qcom/aic200/apdp.mbn",
- [41] = "qcom/aic200/devcfg.mbn",
- [42] = "qcom/aic200/sec.elf",
- [43] = "qcom/aic200/aic200-hlos.elf",
- [49] = "qcom/aic200/shrm.elf",
- [50] = "qcom/aic200/cpucp.elf",
- [51] = "qcom/aic200/aop_devcfg.mbn",
- [54] = "qcom/aic200/qupv3fw.elf",
- [57] = "qcom/aic200/cpucp_dtbs.elf",
- [62] = "qcom/aic200/uefi_dtbs.elf",
- [63] = "qcom/aic200/xbl_ac_config.mbn",
- [64] = "qcom/aic200/tz_ac_config.mbn",
- [65] = "qcom/aic200/hyp_ac_config.mbn",
- [66] = "qcom/aic200/pdp.elf",
- [67] = "qcom/aic200/pdp_cdb.elf",
- [68] = "qcom/aic200/sdi.mbn",
- [69] = "qcom/aic200/dcd.mbn",
- [73] = "qcom/aic200/gearvm.mbn",
- [74] = "qcom/aic200/sti.bin",
- [76] = "qcom/aic200/tz_qti_config.mbn",
- [78] = "qcom/aic200/pvs.bin",
+ const char *fw_folder;
};
static bool is_streaming(struct sahara_context *context)
@@ -795,6 +754,7 @@ static void sahara_read_data_processing(struct work_struct *work)
static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
{
+ const struct mhi_sahara_fw_table *sahara_fw;
struct sahara_context *context;
int ret;
int i;
@@ -807,14 +767,14 @@ static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_
if (!context->rx)
return -ENOMEM;
- if (!strcmp(mhi_dev->mhi_cntrl->name, "AIC200")) {
- context->image_table = aic200_image_table;
- context->table_size = ARRAY_SIZE(aic200_image_table);
- } else {
- context->image_table = aic100_image_table;
- context->table_size = ARRAY_SIZE(aic100_image_table);
- context->non_streaming = true;
- }
+ sahara_fw = mhi_dev->mhi_cntrl->sahara_fw;
+ if (!sahara_fw || !sahara_fw->image_table || !sahara_fw->table_size)
+ return -ENODEV;
+
+ context->image_table = sahara_fw->image_table;
+ context->table_size = sahara_fw->table_size;
+ context->non_streaming = sahara_fw->non_streaming;
+ context->fw_folder = sahara_fw->fw_folder;
/*
* There are two firmware implementations for READ_DATA handling.
diff --git a/include/linux/mhi.h b/include/linux/mhi.h
index 65e8a397cc50b89d045a242aa1164ea7fb8c9696..e1d12e8c1eb80945be6a6d0a5896d92ac221ab81 100644
--- a/include/linux/mhi.h
+++ b/include/linux/mhi.h
@@ -252,6 +252,21 @@ struct mhi_channel_config {
bool wake_capable;
};
+/**
+ * struct mhi_sahara_fw_table - Controller provided sahara firmware mapping
+ * @image_table: Sparse array indexed by Sahara image ID
+ * @table_size: Size of @image_table
+ * @fw_folder: Firmware folder name.
+ * @non_streaming: Streaming feature support (optional)
+ *
+ */
+struct mhi_sahara_fw_table {
+ const char *const *image_table;
+ u32 table_size;
+ const char *fw_folder;
+ bool non_streaming;
+};
+
/**
* struct mhi_event_config - Event ring configuration structure for controller
* @num_elements: The number of elements that can be queued to this ring
@@ -381,6 +396,7 @@ struct mhi_controller_config {
* @no_m3: Device doesn't support M3 state
* @irq_flags: irq flags passed to request_irq (optional)
* @mru: the default MRU for the MHI device
+ * @sahara_fw: Sahara firmware mapping
*
* Fields marked as (required) need to be populated by the controller driver
* before calling mhi_register_controller(). For the fields marked as (optional)
@@ -467,6 +483,7 @@ struct mhi_controller {
bool no_m3;
unsigned long irq_flags;
u32 mru;
+ const struct mhi_sahara_fw_table *sahara_fw;
};
/**
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v9 4/7] bus: mhi: Add QDU100 Sahara variant and firmware fallback
2026-10-13 8:45 [PATCH v9 0/7] Qualcomm Sahara protocol enhancements Kishore Batta
` (2 preceding siblings ...)
2026-10-13 8:45 ` [PATCH v9 3/7] bus: mhi: Centralize Sahara firmware image table selection at probe time Kishore Batta
@ 2026-10-13 8:45 ` Kishore Batta
2026-10-13 9:01 ` sashiko-bot
2026-10-13 8:45 ` [PATCH v9 5/7] bus: mhi: Load DDR training data using device serial number Kishore Batta
` (2 subsequent siblings)
6 siblings, 1 reply; 14+ messages in thread
From: Kishore Batta @ 2026-10-13 8:45 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Jeff Hugo, Carl Vanderlip,
Oded Gabbay, Manivannan Sadhasivam, Randy Dunlap
Cc: linux-doc, linux-kernel, linux-arm-msm, dri-devel, mhi,
Kishore Batta
The Sahara driver currently selects a firmware image table based on the
attached device, but it does not recognize QDU100 devices that expose the
protocol on the SAHARA MHI channel. As a result, the host cannot associate
QDU100 devices with the correct firmware namespace during image transfer.
Extend the probe time variant selection to match the SAHARA MHI channel and
associate it with the QDU100 firmware folder. Add a firmware lookup
fallback for cases where an image does not have an explicit entry in the
device's firmware table. This allows required images to be provisioned by
the platform.
This change only affects devices matched on the SAHARA MHI channel and
does not change behavior for existing AIC100 and AIC200 devices.
Signed-off-by: Kishore Batta <kishore.batta@oss.qualcomm.com>
---
drivers/bus/mhi/host/clients/sahara/sahara.c | 27 ++++++++++++++++--
drivers/bus/mhi/host/pci_generic.c | 41 ++++++++++++++++++++++++++++
2 files changed, 66 insertions(+), 2 deletions(-)
diff --git a/drivers/bus/mhi/host/clients/sahara/sahara.c b/drivers/bus/mhi/host/clients/sahara/sahara.c
index ce4fcbd40baa1b3a3d65d0d10fc9e54b1c7eb04d..a4b3ef197b303cce5a36cbeda272119f0430e619 100644
--- a/drivers/bus/mhi/host/clients/sahara/sahara.c
+++ b/drivers/bus/mhi/host/clients/sahara/sahara.c
@@ -188,6 +188,7 @@ static bool is_streaming(struct sahara_context *context)
static int sahara_find_image(struct sahara_context *context, u32 image_id)
{
+ char *fw_path;
int ret;
if (image_id == context->active_image_id)
@@ -200,8 +201,28 @@ static int sahara_find_image(struct sahara_context *context, u32 image_id)
}
if (image_id >= context->table_size || !context->image_table[image_id]) {
- dev_err(&context->mhi_dev->dev, "request for unknown image: %d\n", image_id);
- return -EINVAL;
+ if (!context->fw_folder) {
+ dev_err(&context->mhi_dev->dev,
+ "Request for unknown image: %u (no fw folder)\n", image_id);
+ return -EINVAL;
+ }
+
+ fw_path = kasprintf(GFP_KERNEL, "qcom/%s/%u",
+ context->fw_folder, image_id);
+ if (!fw_path)
+ return -ENOMEM;
+
+ ret = firmware_request_nowarn(&context->firmware,
+ fw_path,
+ &context->mhi_dev->dev);
+ kfree(fw_path);
+ if (ret) {
+ dev_err(&context->mhi_dev->dev,
+ "request for unknown image: %d\n", image_id);
+ return -EINVAL;
+ }
+ context->active_image_id = image_id;
+ return 0;
}
/*
@@ -869,8 +890,10 @@ static void sahara_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result
static const struct mhi_device_id sahara_mhi_match_table[] = {
{ .chan = "QAIC_SAHARA", },
+ { .chan = "SAHARA", },
{},
};
+MODULE_DEVICE_TABLE(mhi, sahara_mhi_match_table);
static struct mhi_driver sahara_mhi_driver = {
.id_table = sahara_mhi_match_table,
diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c
index 7193a384cca477be9e4f48cdf5f67e680ef55fc5..dcd49cd15074a20cab62a304143abbd0c0614cec 100644
--- a/drivers/bus/mhi/host/pci_generic.c
+++ b/drivers/bus/mhi/host/pci_generic.c
@@ -47,10 +47,12 @@
* of inband wake support (such as sdx24)
* @no_m3: M3 not supported
* @reset_on_remove: Set true for devices that require SoC during driver removal
+ * @sahara_fw: Sahara firmware mapping
*/
struct mhi_pci_dev_info {
const struct mhi_controller_config *config;
const struct mhi_controller_config *vf_config;
+ const struct mhi_sahara_fw_table *sahara_fw;
const char *name;
const char *fw;
const char *edl;
@@ -287,11 +289,49 @@ static const struct mhi_controller_config mhi_qcom_qdu100_config = {
.event_cfg = mhi_qcom_qdu100_events,
};
+static const char * const qdu100_image_table[] = {
+ [5] = "qcom/qdu100/uefi.elf",
+ [8] = "qcom/qdu100/qdsp6sw.mbn",
+ [16] = "qcom/qdu100/efs1.bin",
+ [17] = "qcom/qdu100/efs2.bin",
+ [20] = "qcom/qdu100/efs3.bin",
+ [23] = "qcom/qdu100/aop.mbn",
+ [25] = "qcom/qdu100/tz.mbn",
+ [29] = "qcom/qdu100/zeros_1sector.bin",
+ [33] = "qcom/qdu100/hypvm.mbn",
+ [34] = "qcom/qdu100/mdmddr.mbn",
+ [36] = "qcom/qdu100/multi_image_qti.mbn",
+ [37] = "qcom/qdu100/multi_image.mbn",
+ [38] = "qcom/qdu100/xbl_config.elf",
+ [39] = "qcom/qdu100/abl_userdebug.elf",
+ [40] = "qcom/qdu100/zeros_1sector.bin",
+ [41] = "qcom/qdu100/devcfg.mbn",
+ [42] = "qcom/qdu100/zeros_1sector.bin",
+ [45] = "qcom/qdu100/tools_l.elf",
+ [46] = "qcom/qdu100/Quantum.elf",
+ [47] = "qcom/qdu100/quest.elf",
+ [48] = "qcom/qdu100/xbl_ramdump.elf",
+ [49] = "qcom/qdu100/shrm.elf",
+ [50] = "qcom/qdu100/cpucp.elf",
+ [51] = "qcom/qdu100/aop_devcfg.mbn",
+ [52] = "qcom/qdu100/fw_csm_gsi_3.0.elf",
+ [53] = "qcom/qdu100/qdsp6sw_dtbs.elf",
+ [54] = "qcom/qdu100/qupv3fw.elf",
+};
+
+static const struct mhi_sahara_fw_table qdu100_sahara_fw = {
+ .image_table = qdu100_image_table,
+ .table_size = ARRAY_SIZE(qdu100_image_table),
+ .fw_folder = "qdu100",
+ .non_streaming = false,
+};
+
static const struct mhi_pci_dev_info mhi_qcom_qdu100_info = {
.name = "qcom-qdu100",
.fw = "qcom/qdu100/xbl_s.melf",
.edl_trigger = true,
.config = &mhi_qcom_qdu100_config,
+ .sahara_fw = &qdu100_sahara_fw,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
.vf_dma_data_width = 40,
@@ -1426,6 +1466,7 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
mhi_cntrl->runtime_put = mhi_pci_runtime_put;
mhi_cntrl->mru = info->mru_default;
mhi_cntrl->name = info->name;
+ mhi_cntrl->sahara_fw = info->sahara_fw;
mhi_pdev->no_m3 = info->no_m3;
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v9 5/7] bus: mhi: Load DDR training data using device serial number
2026-10-13 8:45 [PATCH v9 0/7] Qualcomm Sahara protocol enhancements Kishore Batta
` (3 preceding siblings ...)
2026-10-13 8:45 ` [PATCH v9 4/7] bus: mhi: Add QDU100 Sahara variant and firmware fallback Kishore Batta
@ 2026-10-13 8:45 ` Kishore Batta
2026-10-13 9:00 ` sashiko-bot
2026-10-13 8:45 ` [PATCH v9 6/7] bus: mhi: Capture DDR training data via command mode Kishore Batta
2026-10-13 8:45 ` [PATCH v9 7/7] bus: mhi: Expose DDR training data via controller sysfs Kishore Batta
6 siblings, 1 reply; 14+ messages in thread
From: Kishore Batta @ 2026-10-13 8:45 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Jeff Hugo, Carl Vanderlip,
Oded Gabbay, Manivannan Sadhasivam, Randy Dunlap
Cc: linux-doc, linux-kernel, linux-arm-msm, dri-devel, mhi,
Kishore Batta
Devices may provide device specific DDR training data that can be reused
across boot to avoid retraining and reduce boot time. The Sahara driver
currently always falls back to the default DDR training image, even when
serial specific training data is available.
Extend the firmware loading logic for the DDR training image to first
attempt loading a per-device image dervied from the device serial number.
If the serial-specific image is not present, fall back to the existing
default image, preserving current behavior.
This allows reuse of previously generated DDR training data when available,
while keeping the existing training flow unchanged for devices without
saved data or for all other firmware images.
Signed-off-by: Kishore Batta <kishore.batta@oss.qualcomm.com>
---
drivers/bus/mhi/host/clients/sahara/sahara.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/drivers/bus/mhi/host/clients/sahara/sahara.c b/drivers/bus/mhi/host/clients/sahara/sahara.c
index a4b3ef197b303cce5a36cbeda272119f0430e619..62b9c72e826801a77a321991f9bb91af37e0cc8c 100644
--- a/drivers/bus/mhi/host/clients/sahara/sahara.c
+++ b/drivers/bus/mhi/host/clients/sahara/sahara.c
@@ -58,6 +58,7 @@
#define SAHARA_RESET_LENGTH 0x8
#define SAHARA_MEM_DEBUG64_LENGTH 0x18
#define SAHARA_MEM_READ64_LENGTH 0x18
+#define SAHARA_DDR_TRAINING_IMG_ID 34
struct sahara_packet {
__le32 cmd;
@@ -225,6 +226,27 @@ static int sahara_find_image(struct sahara_context *context, u32 image_id)
return 0;
}
+ /* DDR training special case: Try per-serial number file first */
+ if (image_id == SAHARA_DDR_TRAINING_IMG_ID && context->fw_folder) {
+ u32 serial_num = context->mhi_dev->mhi_cntrl->serial_number;
+
+ fw_path = kasprintf(GFP_KERNEL,
+ "qcom/%s/mdmddr_0x%x.mbn",
+ context->fw_folder, serial_num);
+ if (!fw_path)
+ return -ENOMEM;
+
+ ret = firmware_request_nowarn(&context->firmware,
+ fw_path,
+ &context->mhi_dev->dev);
+ kfree(fw_path);
+
+ if (!ret) {
+ context->active_image_id = image_id;
+ return 0;
+ }
+ }
+
/*
* This image might be optional. The device may continue without it.
* Only the device knows. Suppress error messages that could suggest an
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v9 6/7] bus: mhi: Capture DDR training data via command mode
2026-10-13 8:45 [PATCH v9 0/7] Qualcomm Sahara protocol enhancements Kishore Batta
` (4 preceding siblings ...)
2026-10-13 8:45 ` [PATCH v9 5/7] bus: mhi: Load DDR training data using device serial number Kishore Batta
@ 2026-10-13 8:45 ` Kishore Batta
2026-10-13 8:59 ` sashiko-bot
2026-10-13 8:45 ` [PATCH v9 7/7] bus: mhi: Expose DDR training data via controller sysfs Kishore Batta
6 siblings, 1 reply; 14+ messages in thread
From: Kishore Batta @ 2026-10-13 8:45 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Jeff Hugo, Carl Vanderlip,
Oded Gabbay, Manivannan Sadhasivam, Randy Dunlap
Cc: linux-doc, linux-kernel, linux-arm-msm, dri-devel, mhi,
Kishore Batta
During early boot, devices may perform DDR training and produce training
data that can be reused on subsequent boots to reduce initialization
time. The Sahara protocol provides a command mode flow to transfer this
training data to the host, but the driver currently does not handle
command mode and drops the training payload.
Add Sahara command mode support to retrieve DDR training data from the
device. When the device enters command mode and sends CMD_READY, query
the support command list(ID 8) and request DDR training data(ID 9) using
EXECUTE and EXECUTE_DATA as defined by protocol. Allocate receive buffers
based on the reported response size and copy the raw payload directly from
the MHI DL completion callback.
Store the captured training data in controller-scoped memory using devres,
so it remains available after Sahara channel teardown. Also distinguish
raw payload completion from control packets in the DL callback, avoiding
misinterpretation of training data as protocol messages, and requeue
the RX buffer after switching back to IMAGE_TX_PENDING to allow the
boot flow to continue.
Signed-off-by: Kishore Batta <kishore.batta@oss.qualcomm.com>
---
drivers/bus/mhi/host/clients/sahara/sahara.c | 326 ++++++++++++++++++++++++++-
1 file changed, 319 insertions(+), 7 deletions(-)
diff --git a/drivers/bus/mhi/host/clients/sahara/sahara.c b/drivers/bus/mhi/host/clients/sahara/sahara.c
index 62b9c72e826801a77a321991f9bb91af37e0cc8c..aa684355b7a31674f81a43a3783d5c4158b824b1 100644
--- a/drivers/bus/mhi/host/clients/sahara/sahara.c
+++ b/drivers/bus/mhi/host/clients/sahara/sahara.c
@@ -5,10 +5,13 @@
*/
#include <linux/devcoredump.h>
+#include <linux/device.h>
+#include <linux/device/devres.h>
#include <linux/firmware.h>
#include <linux/limits.h>
#include <linux/mhi.h>
#include <linux/minmax.h>
+#include <linux/mutex.h>
#include <linux/overflow.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
@@ -58,7 +61,15 @@
#define SAHARA_RESET_LENGTH 0x8
#define SAHARA_MEM_DEBUG64_LENGTH 0x18
#define SAHARA_MEM_READ64_LENGTH 0x18
+#define SAHARA_COMMAND_READY_LENGTH 0x8
+#define SAHARA_COMMAND_EXEC_RESP_LENGTH 0x10
+#define SAHARA_COMMAND_EXECUTE_LENGTH 0xc
+#define SAHARA_COMMAND_EXEC_DATA_LENGTH 0xc
+#define SAHARA_SWITCH_MODE_LENGTH 0xc
+#define SAHARA_EXEC_CMD_GET_COMMAND_ID_LIST 0x8
+#define SAHARA_EXEC_CMD_GET_TRAINING_DATA 0x9
#define SAHARA_DDR_TRAINING_IMG_ID 34
+#define SAHARA_NUM_CMD_BUF SAHARA_NUM_TX_BUF
struct sahara_packet {
__le32 cmd;
@@ -94,6 +105,19 @@ struct sahara_packet {
__le64 memory_address;
__le64 memory_length;
} memory_read64;
+ struct {
+ __le32 client_command;
+ } command_execute;
+ struct {
+ __le32 client_command;
+ __le32 response_length;
+ } command_execute_resp;
+ struct {
+ __le32 client_command;
+ } command_exec_data;
+ struct {
+ __le32 mode;
+ } mode_switch;
};
};
@@ -160,6 +184,7 @@ struct sahara_context {
struct work_struct fw_work;
struct work_struct dump_work;
struct work_struct read_data_work;
+ struct work_struct cmd_work;
struct mhi_device *mhi_dev;
const char * const *image_table;
u32 table_size;
@@ -180,6 +205,24 @@ struct sahara_context {
bool is_mem_dump_mode;
bool non_streaming;
const char *fw_folder;
+ bool is_cmd_mode;
+ bool receiving_training_data;
+ size_t training_size;
+ size_t training_rcvd;
+ u32 training_nbuf;
+ char *cmd_buff[SAHARA_NUM_CMD_BUF];
+};
+
+/*
+ * Controller-scoped training data store (per MHI controller device).
+ * Stored as devres resource on mhi_dev->mhi_cntrl->mhi_dev->dev.
+ */
+struct sahara_cntrl_training_data {
+ struct mutex lock; /* Protects data, size, copied and receiving */
+ void *data;
+ size_t size;
+ size_t copied;
+ bool receiving;
};
static bool is_streaming(struct sahara_context *context)
@@ -187,6 +230,48 @@ static bool is_streaming(struct sahara_context *context)
return !context->non_streaming;
}
+static void sahara_cntrl_training_release(struct device *dev, void *res)
+{
+ struct sahara_cntrl_training_data *ct = res;
+
+ mutex_lock(&ct->lock);
+ kfree(ct->data);
+ ct->data = NULL;
+ ct->size = 0;
+ ct->copied = 0;
+ ct->receiving = false;
+ mutex_unlock(&ct->lock);
+}
+
+static int sahara_cntrl_training_match(struct device *dev, void *res, void *match_data)
+{
+ /* Exactly one instance per controller */
+ return 1;
+}
+
+static struct sahara_cntrl_training_data *sahara_cntrl_training_get(struct device *dev)
+{
+ struct sahara_cntrl_training_data *ct;
+
+ ct = devres_find(dev, sahara_cntrl_training_release,
+ sahara_cntrl_training_match, NULL);
+ if (ct)
+ return ct;
+
+ ct = devres_alloc(sahara_cntrl_training_release, sizeof(*ct), GFP_KERNEL);
+ if (!ct)
+ return NULL;
+
+ mutex_init(&ct->lock);
+ ct->data = NULL;
+ ct->size = 0;
+ ct->copied = 0;
+ ct->receiving = false;
+
+ devres_add(dev, ct);
+ return ct;
+}
+
static int sahara_find_image(struct sahara_context *context, u32 image_id)
{
char *fw_path;
@@ -280,6 +365,11 @@ static void sahara_send_reset(struct sahara_context *context)
context->is_mem_dump_mode = false;
context->read_data_offset = 0;
context->read_data_length = 0;
+ context->is_cmd_mode = false;
+ context->receiving_training_data = false;
+ context->training_size = 0;
+ context->training_rcvd = 0;
+ context->training_nbuf = 0;
context->tx[0]->cmd = cpu_to_le32(SAHARA_RESET_CMD);
context->tx[0]->length = cpu_to_le32(SAHARA_RESET_LENGTH);
@@ -315,7 +405,8 @@ static void sahara_hello(struct sahara_context *context)
if (le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_IMAGE_TX_PENDING &&
le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_IMAGE_TX_COMPLETE &&
- le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_MEMORY_DEBUG) {
+ le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_MEMORY_DEBUG &&
+ le32_to_cpu(context->rx->hello.mode) != SAHARA_MODE_COMMAND) {
dev_err(&context->mhi_dev->dev, "Unsupported hello packet - mode %d\n",
le32_to_cpu(context->rx->hello.mode));
return;
@@ -334,6 +425,153 @@ static void sahara_hello(struct sahara_context *context)
dev_err(&context->mhi_dev->dev, "Unable to send hello response %d\n", ret);
}
+static void sahara_switch_mode_to_img_tx(struct sahara_context *context)
+{
+ int ret;
+
+ context->tx[0]->cmd = cpu_to_le32(SAHARA_SWITCH_MODE_CMD);
+ context->tx[0]->length = cpu_to_le32(SAHARA_SWITCH_MODE_LENGTH);
+ context->tx[0]->mode_switch.mode = cpu_to_le32(SAHARA_MODE_IMAGE_TX_PENDING);
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
+ SAHARA_SWITCH_MODE_LENGTH, MHI_EOT);
+
+ if (ret)
+ dev_err(&context->mhi_dev->dev, "Unable to send mode switch %d\n", ret);
+}
+
+static void sahara_command_execute(struct sahara_context *context, u32 client_command)
+{
+ int ret;
+
+ context->tx[0]->cmd = cpu_to_le32(SAHARA_EXECUTE_CMD);
+ context->tx[0]->length = cpu_to_le32(SAHARA_COMMAND_EXECUTE_LENGTH);
+ context->tx[0]->command_execute.client_command = cpu_to_le32(client_command);
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
+ SAHARA_COMMAND_EXECUTE_LENGTH, MHI_EOT);
+ if (ret)
+ dev_err(&context->mhi_dev->dev, "Unable to send command execute %d\n", ret);
+}
+
+static void sahara_command_execute_data(struct sahara_context *context, u32 client_command)
+{
+ int ret;
+
+ context->tx[0]->cmd = cpu_to_le32(SAHARA_EXECUTE_DATA_CMD);
+ context->tx[0]->length = cpu_to_le32(SAHARA_COMMAND_EXEC_DATA_LENGTH);
+ context->tx[0]->command_exec_data.client_command = cpu_to_le32(client_command);
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE, context->tx[0],
+ SAHARA_COMMAND_EXEC_DATA_LENGTH, MHI_EOT);
+ if (ret)
+ dev_err(&context->mhi_dev->dev, "Unable to send execute data %d\n", ret);
+}
+
+static void sahara_command_ready(struct sahara_context *context)
+{
+ if (le32_to_cpu(context->rx->length) != SAHARA_COMMAND_READY_LENGTH) {
+ dev_err(&context->mhi_dev->dev,
+ "Malformed command ready packet - length %u\n",
+ le32_to_cpu(context->rx->length));
+ return;
+ }
+
+ context->is_cmd_mode = true;
+ context->receiving_training_data = false;
+
+ sahara_command_execute(context, SAHARA_EXEC_CMD_GET_COMMAND_ID_LIST);
+}
+
+static void sahara_command_execute_resp(struct sahara_context *context)
+{
+ struct device *dev = &context->mhi_dev->mhi_cntrl->mhi_dev->dev;
+ struct sahara_cntrl_training_data *ct;
+ u32 client_cmd, resp_len;
+ int ret;
+ u64 remaining;
+ u32 i;
+
+ if (le32_to_cpu(context->rx->length) != SAHARA_COMMAND_EXEC_RESP_LENGTH ||
+ le32_to_cpu(context->rx->command_execute_resp.response_length) < 0) {
+ dev_err(&context->mhi_dev->dev,
+ "Malformed command execute resp packet - length %d\n",
+ le32_to_cpu(context->rx->length));
+ return;
+ }
+
+ client_cmd = le32_to_cpu(context->rx->command_execute_resp.client_command);
+ resp_len = le32_to_cpu(context->rx->command_execute_resp.response_length);
+
+ sahara_command_execute_data(context, client_cmd);
+
+ if (client_cmd == SAHARA_EXEC_CMD_GET_COMMAND_ID_LIST) {
+ sahara_command_execute(context, SAHARA_EXEC_CMD_GET_TRAINING_DATA);
+ return;
+ }
+
+ if (client_cmd != SAHARA_EXEC_CMD_GET_TRAINING_DATA)
+ return;
+
+ ct = sahara_cntrl_training_get(dev);
+ if (!ct) {
+ context->is_cmd_mode = false;
+ sahara_switch_mode_to_img_tx(context);
+ return;
+ }
+
+ mutex_lock(&ct->lock);
+ kfree(ct->data);
+ ct->data = kzalloc(resp_len, GFP_KERNEL);
+ ct->size = resp_len;
+ ct->copied = 0;
+ ct->receiving = true;
+ mutex_unlock(&ct->lock);
+
+ if (!ct->data) {
+ context->is_cmd_mode = false;
+ sahara_switch_mode_to_img_tx(context);
+ return;
+ }
+
+ context->training_size = resp_len;
+ context->training_rcvd = 0;
+ context->receiving_training_data = true;
+
+ remaining = resp_len;
+ for (i = 0; i < SAHARA_NUM_CMD_BUF && remaining; i++) {
+ size_t pkt = min_t(size_t, remaining, SAHARA_PACKET_MAX_SIZE);
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_FROM_DEVICE,
+ context->cmd_buff[i], pkt,
+ (remaining <= pkt) ? MHI_EOT : MHI_CHAIN);
+ if (ret)
+ break;
+
+ remaining -= pkt;
+ }
+
+ context->training_nbuf = i;
+}
+
+static void sahara_command_processing(struct work_struct *work)
+{
+ struct sahara_context *context = container_of(work, struct sahara_context, cmd_work);
+ int ret;
+
+ if (le32_to_cpu(context->rx->cmd) == SAHARA_EXECUTE_RESP_CMD)
+ sahara_command_execute_resp(context);
+
+ if (!context->receiving_training_data) {
+ ret = mhi_queue_buf(context->mhi_dev, DMA_FROM_DEVICE,
+ context->rx, SAHARA_PACKET_MAX_SIZE, MHI_EOT);
+
+ if (ret)
+ dev_err(&context->mhi_dev->dev,
+ "Unable to requeue rx buf %d\n", ret);
+ }
+}
+
static int read_data_helper(struct sahara_context *context, int buf_index)
{
enum mhi_flags mhi_flag;
@@ -560,6 +798,9 @@ static void sahara_processing(struct work_struct *work)
case SAHARA_MEM_DEBUG64_CMD:
sahara_memory_debug64(context);
break;
+ case SAHARA_CMD_READY_CMD:
+ sahara_command_ready(context);
+ break;
default:
dev_err(&context->mhi_dev->dev, "Unknown command %d\n",
le32_to_cpu(context->rx->cmd));
@@ -860,6 +1101,20 @@ static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_
INIT_WORK(&context->fw_work, sahara_processing);
INIT_WORK(&context->dump_work, sahara_dump_processing);
INIT_WORK(&context->read_data_work, sahara_read_data_processing);
+ INIT_WORK(&context->cmd_work, sahara_command_processing);
+
+ for (i = 0; i < SAHARA_NUM_CMD_BUF; i++) {
+ context->cmd_buff[i] = devm_kzalloc(&mhi_dev->dev,
+ SAHARA_PACKET_MAX_SIZE, GFP_KERNEL);
+ if (!context->cmd_buff[i])
+ return -ENOMEM;
+ }
+
+ context->is_cmd_mode = false;
+ context->receiving_training_data = false;
+ context->training_size = 0;
+ context->training_rcvd = 0;
+ context->training_nbuf = 0;
context->active_image_id = SAHARA_IMAGE_ID_NONE;
dev_set_drvdata(&mhi_dev->dev, context);
@@ -883,6 +1138,7 @@ static void sahara_mhi_remove(struct mhi_device *mhi_dev)
cancel_work_sync(&context->fw_work);
cancel_work_sync(&context->dump_work);
+ cancel_work_sync(&context->cmd_work);
vfree(context->mem_dump);
sahara_release_image(context);
mhi_unprepare_from_transfer(mhi_dev);
@@ -899,15 +1155,71 @@ static void sahara_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result
static void sahara_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
{
struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev);
+ struct sahara_cntrl_training_data *ct;
+ struct device *dev;
+ size_t copy;
+ int ret;
+ u32 i;
+
+ if (mhi_result->transaction_status)
+ return;
+
+ /*
+ * Raw training payload completions arrive for cmd_buff[] buffers.
+ * Do not schedule cmd_work for those.
+ */
+ if (context->is_cmd_mode && context->receiving_training_data &&
+ mhi_result->buf_addr != context->rx) {
+ dev = &context->mhi_dev->mhi_cntrl->mhi_dev->dev;
+ ct = sahara_cntrl_training_get(dev);
+ if (!ct)
+ return;
- if (!mhi_result->transaction_status) {
- context->rx_size = mhi_result->bytes_xferd;
- if (context->is_mem_dump_mode)
- schedule_work(&context->dump_work);
- else
- schedule_work(&context->fw_work);
+ for (i = 0; i < context->training_nbuf; i++) {
+ if (mhi_result->buf_addr == context->cmd_buff[i]) {
+ mutex_lock(&ct->lock);
+ copy = min_t(size_t, mhi_result->bytes_xferd,
+ ct->size - ct->copied);
+ memcpy((u8 *)ct->data + ct->copied,
+ mhi_result->buf_addr, copy);
+ ct->copied += copy;
+ mutex_unlock(&ct->lock);
+
+ context->training_rcvd += copy;
+
+ if (context->training_rcvd >= context->training_size) {
+ mutex_lock(&ct->lock);
+ ct->receiving = false;
+ mutex_unlock(&ct->lock);
+
+ context->receiving_training_data = false;
+ context->is_cmd_mode = false;
+
+ sahara_switch_mode_to_img_tx(context);
+ ret = mhi_queue_buf(context->mhi_dev,
+ DMA_FROM_DEVICE,
+ context->rx,
+ SAHARA_PACKET_MAX_SIZE,
+ MHI_EOT);
+ if (ret)
+ dev_err(&context->mhi_dev->dev,
+ "Unable to requeue rx buf %d\n", ret);
+ }
+ return;
+ }
+ }
+ return;
}
+ /* Normal Rx completion */
+ context->rx_size = mhi_result->bytes_xferd;
+ if (context->is_mem_dump_mode)
+ schedule_work(&context->dump_work);
+ else if (context->is_cmd_mode)
+ schedule_work(&context->cmd_work);
+ else
+ schedule_work(&context->fw_work);
+
}
static const struct mhi_device_id sahara_mhi_match_table[] = {
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v9 7/7] bus: mhi: Expose DDR training data via controller sysfs
2026-10-13 8:45 [PATCH v9 0/7] Qualcomm Sahara protocol enhancements Kishore Batta
` (5 preceding siblings ...)
2026-10-13 8:45 ` [PATCH v9 6/7] bus: mhi: Capture DDR training data via command mode Kishore Batta
@ 2026-10-13 8:45 ` Kishore Batta
2026-10-13 8:58 ` sashiko-bot
6 siblings, 1 reply; 14+ messages in thread
From: Kishore Batta @ 2026-10-13 8:45 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Jeff Hugo, Carl Vanderlip,
Oded Gabbay, Manivannan Sadhasivam, Randy Dunlap
Cc: linux-doc, linux-kernel, linux-arm-msm, dri-devel, mhi,
Kishore Batta
DDR training data captured during Sahara command mode needs to be
accessible to userspace so it can be persisted and reused on subsequent
boots. Currently, the training data is stored internally in the driver
but has no external visibility once the Sahara channel is torn down.
Expose the captured DDR training data via a read-only binary sysfs
attribute on the MHI controller device:
/sys/bus/mhi/devices/<mhi_cntrl>/ddr_training_data
The sysfs read callback serves data directly from controller scoped storage
and protects access with the controller training data lock. The attribute
lifetime is tied to the controller device via devres, allowing the data to
remain readable after Sahara channel teardown and ensuring automatic
cleanup when controller device is removed.
Userspace flow:
1. For each controller device, userspace reads the ddr_training_data sysfs
attribute.
2. If the read returns non-zero data, userspace persists it using a
serial specific filename (for example, mdmddr_0x<serial_no>.mbn).
3. On subsequent boots, the Sahara driver attempts to load this serial
specific DDR training image before falling back to the default
training image, restoring DDR calibration data and avoiding retraining.
Add ABI documentation for the DDR training data sysfs attribute exposed by
Sahara MHI driver.
Signed-off-by: Kishore Batta <kishore.batta@oss.qualcomm.com>
---
.../ABI/testing/sysfs-bus-mhi-ddr_training_data | 19 +++++++
drivers/bus/mhi/host/clients/sahara/sahara.c | 63 ++++++++++++++++++++++
2 files changed, 82 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-bus-mhi-ddr_training_data b/Documentation/ABI/testing/sysfs-bus-mhi-ddr_training_data
new file mode 100644
index 0000000000000000000000000000000000000000..810b487b5a5fdba133d81255f9879844e3938a10
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-mhi-ddr_training_data
@@ -0,0 +1,19 @@
+What: /sys/bus/mhi/devices/<mhi-cntrl>/ddr_training_data
+
+Date: March 2026
+
+Contact: Kishore Batta <kishore.batta@oss.qualcomm.com>
+
+Description: Contains the DDR training data for the Qualcomm device
+ connected. MHI driver populates different controller
+ nodes for each device. The DDR training data is exposed
+ to userspace to read and save the training data file to
+ the filesystem. In the subsequent boot up of the device,
+ the training data is restored from host to device
+ optimizing the boot up time of the device.
+
+Usage: Example for reading DDR training data:
+ cat /sys/bus/mhi/devices/mhi0/ddr_training_data
+
+Permissions: The file permissions are set to 0444 allowing read
+ access.
diff --git a/drivers/bus/mhi/host/clients/sahara/sahara.c b/drivers/bus/mhi/host/clients/sahara/sahara.c
index aa684355b7a31674f81a43a3783d5c4158b824b1..be5318fc5e221d0c91fdd9c4b6c908ef1fa4e852 100644
--- a/drivers/bus/mhi/host/clients/sahara/sahara.c
+++ b/drivers/bus/mhi/host/clients/sahara/sahara.c
@@ -272,6 +272,67 @@ static struct sahara_cntrl_training_data *sahara_cntrl_training_get(struct devic
return ct;
}
+static ssize_t ddr_training_data_read(struct file *filp, struct kobject *kobj,
+ const struct bin_attribute *attr, char *buf,
+ loff_t offset, size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct sahara_cntrl_training_data *ct;
+ size_t available;
+
+ ct = sahara_cntrl_training_get(dev);
+ if (!ct)
+ return -ENODEV;
+
+ mutex_lock(&ct->lock);
+
+ /* No complete data yet or offset past end */
+ if (!ct->data || ct->receiving || ct->copied != ct->size ||
+ offset >= ct->size) {
+ mutex_unlock(&ct->lock);
+ return 0;
+ }
+
+ available = ct->size - offset;
+ count = min(count, available);
+ memcpy(buf, (u8 *)ct->data + offset, count);
+
+ mutex_unlock(&ct->lock);
+
+ return count;
+}
+static BIN_ATTR_RO(ddr_training_data, 0);
+
+static void sahara_sysfs_devres_release(struct device *dev, void *res)
+{
+ device_remove_bin_file(dev, &bin_attr_ddr_training_data);
+}
+
+static void sahara_sysfs_create(struct mhi_device *mhi_dev)
+{
+ struct device *dev = &mhi_dev->mhi_cntrl->mhi_dev->dev;
+ void *cookie;
+ int ret;
+
+ if (devres_find(dev, sahara_sysfs_devres_release, NULL, NULL))
+ return;
+
+ ret = device_create_bin_file(dev, &bin_attr_ddr_training_data);
+ if (ret) {
+ dev_warn(&mhi_dev->dev,
+ "Failed to create DDR training sysfs node (%d)\n", ret);
+ return;
+ }
+
+ cookie = devres_alloc(sahara_sysfs_devres_release, 1, GFP_KERNEL);
+ if (!cookie) {
+ device_remove_bin_file(dev, &bin_attr_ddr_training_data);
+ return;
+ }
+
+ devres_add(dev, cookie);
+}
+
static int sahara_find_image(struct sahara_context *context, u32 image_id)
{
char *fw_path;
@@ -1129,6 +1190,8 @@ static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_
return ret;
}
+ sahara_sysfs_create(mhi_dev);
+
return 0;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v9 1/7] Add documentation for Sahara protocol
2026-10-13 8:45 ` [PATCH v9 1/7] Add documentation for Sahara protocol Kishore Batta
@ 2026-10-13 8:51 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-13 8:51 UTC (permalink / raw)
To: Kishore Batta; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] doc: typographical errors in Sahara protocol documentation
--
commit 8d07d5d5d03da68e6f60aacfd0b9952197576e1b
Author: Kishore Batta <kishore.batta@oss.qualcomm.com>
Add documentation for Sahara protocol
Introduce documentation for the Sahara protocol, describing its
operational modes and their respective functions. The image transfer mode
enables firmware transfer from host to device. The memory debug mode
allows extraction of device memory contents to host.
> diff --git a/Documentation/mhi/sahara_protocol.rst b/Documentation/mhi/sahara_protocol.rst
> new file mode 100644
> index 0000000000000..3190e3e5a2820
> --- /dev/null
> +++ b/Documentation/mhi/sahara_protocol.rst
[ ... ]
> +| 0x13 | Reset Sahara | Host | Resets Sahara state machine and enters |
> +| | sate machine | | Sahara entry without target reset |
> +| | packet | | |
[Severity: Low]
Does this code contain a typo? It looks like "sate machine" should be
"state machine".
[ ... ]
> ++-----------------------------+---------+------------------------------------+
> +| SAHARA_MODE_MEMORY_DBEUG | 0x2 | Memory debug mode. The host should |
> +| | | prepare to receive a memory dump |
[Severity: Low]
Does this code contain a typo? It looks like "SAHARA_MODE_MEMORY_DBEUG" should
be "SAHARA_MODE_MEMORY_DEBUG".
[ ... ]
> +host opens an output file and waits to receive the data packets. After the
> +packet is received, the content from the data pcket is written to the output
> +file, The format of the write data packet is as follows:
[Severity: Low]
Does this code contain typos? It looks like "pcket" should be "packet", and
the comma after "output file" should be a period.
[ ... ]
> +6. The target directly transfers the data to the destination address without
> + any software processing or temporarily buffering of the data in system
> + memory by transferring the image header to the targert and setting the
> + receive buffer for the data as the destination address in system memory.
[Severity: Low]
Does this code contain a typo? It looks like "targert" should be "target".
[ ... ]
> +8. The host sends a done packet to allow the target to exit the protocol after
> + it receives a successgul end of image transfer packet.
[Severity: Low]
Does this code contain a typo? It looks like "successgul" should be
"successful".
[ ... ]
> + | MEMORY_READ |
> + | (Address from region 1 ,|
> + | size of region 1) |
> + |-------------------------->|
> + | RAW_DATA |
> + | (size of region 1) |
> + |<--------------------------|
> + | MEMORY_READ |
> + | (Address from region 2 ,|
> + | size of region 0) |
> + |-------------------------->|
> + | RAW_DATA |
> + | (size of region 2) |
> + |<--------------------------|
[Severity: Low]
Does this diagram contain a copy-paste error? For region 2, the memory read
packet requests "size of region 0" instead of "size of region 2".
[ ... ]
> +Subsequent boot scenario with valid DDR calibration data
> +--------------------------------------------------------
> +
> +The below firgure shows the subsequent boot scenario with valid DDR calibration
> +data process being loaded from host to target.
[Severity: Low]
Does this code contain a typo? It looks like "firgure" should be "figure".
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261013-sahara_protocol_new_v2-v9-0-dc9f24357c4a@oss.qualcomm.com?part=1
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v9 7/7] bus: mhi: Expose DDR training data via controller sysfs
2026-10-13 8:45 ` [PATCH v9 7/7] bus: mhi: Expose DDR training data via controller sysfs Kishore Batta
@ 2026-10-13 8:58 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-13 8:58 UTC (permalink / raw)
To: Kishore Batta; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] mhi: sahara: module unload UAF due to controller-scoped devres and sysfs
- [High] mhi: sahara: sleeping mutex_lock() in atomic dl_xfer_cb callback
- [High] bus: mhi: sahara: UAF in ddr_training_data_read due to devres release order
- [Medium] mhi: sahara: lockless devres creation in sysfs read causes memory leak
- [Low] sahara: unsigned length validation checked for < 0 is dead code
--
commit 17df5ced69df7ffe5c236bc241ec6e9939859682
Author: Kishore Batta <kishore.batta@oss.qualcomm.com>
bus: mhi: Expose DDR training data via controller sysfs
This commit exposes DDR training data through a new sysfs binary attribute
attached to the MHI controller device. This enables userspace to persist
the captured training data and reload it on subsequent boots to optimize
boot times.
> diff --git a/drivers/bus/mhi/host/clients/sahara/sahara.c b/drivers/bus/mhi/host/clients/sahara/sahara.c
> index aa684355b7a31..be5318fc5e221 100644
> --- a/drivers/bus/mhi/host/clients/sahara/sahara.c
> +++ b/drivers/bus/mhi/host/clients/sahara/sahara.c
> @@ -272,6 +272,67 @@ static struct sahara_cntrl_training_data *sahara_cntrl_training_get(struct devic
> return ct;
> }
>
> +static ssize_t ddr_training_data_read(struct file *filp, struct kobject *kobj,
> + const struct bin_attribute *attr, char *buf,
> + loff_t offset, size_t count)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct sahara_cntrl_training_data *ct;
> + size_t available;
> +
> + ct = sahara_cntrl_training_get(dev);
> + if (!ct)
> + return -ENODEV;
> +
> + mutex_lock(&ct->lock);
[Severity: High]
In sahara_sysfs_create(), the sysfs devres cookie is added to the device
during probe. However, sahara_cntrl_training_get() dynamically allocates and
adds the ct structure to the same device's devres list lazily on first use.
Because ct is added to devres after the sysfs cookie, the LIFO nature of
devres_release_all() guarantees ct is freed before the sysfs file is removed.
Could a concurrent sysfs read fetch the ct pointer, get preempted, and attempt
to lock ct->lock after devres has already freed it, leading to a
use-after-free during device removal?
> +
> + /* No complete data yet or offset past end */
> + if (!ct->data || ct->receiving || ct->copied != ct->size ||
> + offset >= ct->size) {
> + mutex_unlock(&ct->lock);
> + return 0;
> + }
> +
> + available = ct->size - offset;
> + count = min(count, available);
> + memcpy(buf, (u8 *)ct->data + offset, count);
> +
> + mutex_unlock(&ct->lock);
> +
> + return count;
> +}
> +static BIN_ATTR_RO(ddr_training_data, 0);
> +
> +static void sahara_sysfs_devres_release(struct device *dev, void *res)
> +{
> + device_remove_bin_file(dev, &bin_attr_ddr_training_data);
> +}
> +
> +static void sahara_sysfs_create(struct mhi_device *mhi_dev)
> +{
> + struct device *dev = &mhi_dev->mhi_cntrl->mhi_dev->dev;
> + void *cookie;
> + int ret;
> +
> + if (devres_find(dev, sahara_sysfs_devres_release, NULL, NULL))
> + return;
> +
> + ret = device_create_bin_file(dev, &bin_attr_ddr_training_data);
> + if (ret) {
> + dev_warn(&mhi_dev->dev,
> + "Failed to create DDR training sysfs node (%d)\n", ret);
> + return;
> + }
> +
> + cookie = devres_alloc(sahara_sysfs_devres_release, 1, GFP_KERNEL);
> + if (!cookie) {
> + device_remove_bin_file(dev, &bin_attr_ddr_training_data);
> + return;
> + }
> +
> + devres_add(dev, cookie);
[Severity: High]
The sahara driver can be built as a loadable module. By attaching the
ddr_training_data sysfs file and devres cleanup cookie to the parent
controller device (mhi_cntrl->mhi_dev->dev), they outlive the sahara client
device.
When the sahara module is unloaded, the sysfs file and devres objects are
not detached. Once the module is unmapped, userspace reading the stale file
or the controller device being removed will cause the kernel to jump to the
freed ddr_training_data_read or sahara_sysfs_devres_release functions.
Does attaching the sysfs node and devres callback to the parent MHI
controller cause a use-after-free on module unload?
> +}
[ ... ]
I also noticed a few other issues in related Sahara functions that were
introduced in this series:
[Severity: Medium]
In sahara_cntrl_training_get(), we have a check-then-act sequence:
drivers/bus/mhi/host/clients/sahara/sahara.c:sahara_cntrl_training_get() {
...
ct = devres_find(dev, sahara_cntrl_training_release,
sahara_cntrl_training_match, NULL);
if (ct)
return ct;
ct = devres_alloc(sahara_cntrl_training_release, sizeof(*ct), GFP_KERNEL);
...
devres_add(dev, ct);
return ct;
}
Since ddr_training_data_read() can be invoked concurrently by multiple
userspace threads, multiple threads could see devres_find() return NULL
and allocate separate ct structures.
Does this permanently leak memory and break synchronization since different
threads would use different mutexes?
[Severity: High]
In sahara_mhi_dl_xfer_cb(), the MHI transfer callback executes in the MHI
event tasklet, which is an atomic context:
drivers/bus/mhi/host/clients/sahara/sahara.c:sahara_mhi_dl_xfer_cb() {
...
ct = sahara_cntrl_training_get(dev);
if (!ct)
return;
for (i = 0; i < context->training_nbuf; i++) {
if (mhi_result->buf_addr == context->cmd_buff[i]) {
mutex_lock(&ct->lock);
...
}
Will unconditionally invoking mutex_lock() here result in a sleep-in-atomic bug?
[Severity: Low]
In sahara_command_execute_resp(), there is a negative length validation:
drivers/bus/mhi/host/clients/sahara/sahara.c:sahara_command_execute_resp() {
...
if (le32_to_cpu(context->rx->length) != SAHARA_COMMAND_EXEC_RESP_LENGTH ||
le32_to_cpu(context->rx->command_execute_resp.response_length) < 0) {
...
}
Since le32_to_cpu() returns an unsigned 32-bit integer, checking if
response_length is < 0 will always be false.
While an unexpectedly large length would gracefully fail the subsequent
memory allocation, should this check be corrected to prevent bypassing the
validation?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261013-sahara_protocol_new_v2-v9-0-dc9f24357c4a@oss.qualcomm.com?part=7
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v9 6/7] bus: mhi: Capture DDR training data via command mode
2026-10-13 8:45 ` [PATCH v9 6/7] bus: mhi: Capture DDR training data via command mode Kishore Batta
@ 2026-10-13 8:59 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-13 8:59 UTC (permalink / raw)
To: Kishore Batta; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 9 potential issue(s) to consider:
- [High] mhi: Sleeping in atomic context due to mutex_lock() in MHI transfer callback
- [High] mhi: Data corruption due to concurrent modification of shared TX buffer
- [High] mhi: TOCTOU race condition dropping valid RX completions
- [High] bus: mhi: sleeping in atomic context during DL transfer callback
- [High] bus: mhi: TX buffer reuse race and raw payload misinterpretation in Sahara
- [High] mhi: UAF / crash due to parent devres callback on module unload
- [Medium] mhi: Permanent channel hang due to un-requeued RX buffers for large transfers
- [Medium] mhi: Dead code in bounds validation due to unsigned comparison
- [Medium] mhi: Permanent channel hang due to unhandled zero-length response
--
commit 80f9d9bf2b27a92703cf2016cc04d3f992e75657
Author: Kishore Batta <kishore.batta@oss.qualcomm.com>
bus: mhi: Capture DDR training data via command mode
During early boot, devices may perform DDR training and produce training
data that can be reused on subsequent boots to reduce initialization
time. The Sahara protocol provides a command mode flow to transfer this
training data to the host, but the driver currently does not handle
command mode and drops the training payload.
> diff --git a/drivers/bus/mhi/host/clients/sahara/sahara.c b/drivers/bus/mhi/host/clients/sahara/sahara.c
> index 62b9c72e82680..aa684355b7a31 100644
> --- a/drivers/bus/mhi/host/clients/sahara/sahara.c
> +++ b/drivers/bus/mhi/host/clients/sahara/sahara.c
[ ... ]
> +static struct sahara_cntrl_training_data *sahara_cntrl_training_get(struct device *dev)
> +{
> + struct sahara_cntrl_training_data *ct;
> +
> + ct = devres_find(dev, sahara_cntrl_training_release,
> + sahara_cntrl_training_match, NULL);
> + if (ct)
> + return ct;
> +
> + ct = devres_alloc(sahara_cntrl_training_release, sizeof(*ct), GFP_KERNEL);
[Severity: High]
Does allocating devres memory with GFP_KERNEL here cause a sleeping in atomic
context bug?
This function is called from sahara_mhi_dl_xfer_cb(), which is executed from
the MHI event tasklet under a spinlock, creating an atomic context.
[Severity: High]
Can attaching this devres to the parent MHI controller device cause a
use-after-free on module unload?
Since the sahara module does not detach this devres on removal to persist the
data, when the parent controller device is later unbound, devres_release_all()
will call the sahara_cntrl_training_release pointer which resides in the
unloaded module's unmapped memory.
[ ... ]
> +static void sahara_command_execute_resp(struct sahara_context *context)
> +{
> + struct device *dev = &context->mhi_dev->mhi_cntrl->mhi_dev->dev;
> + struct sahara_cntrl_training_data *ct;
> + u32 client_cmd, resp_len;
> + int ret;
> + u64 remaining;
> + u32 i;
> +
> + if (le32_to_cpu(context->rx->length) != SAHARA_COMMAND_EXEC_RESP_LENGTH ||
> + le32_to_cpu(context->rx->command_execute_resp.response_length) < 0) {
[Severity: Medium]
Is this bounds validation effective?
Since le32_to_cpu() returns an unsigned u32, checking if it is < 0 is
tautologically false and bypasses the validation.
> + dev_err(&context->mhi_dev->dev,
> + "Malformed command execute resp packet - length %d\n",
> + le32_to_cpu(context->rx->length));
> + return;
> + }
> +
> + client_cmd = le32_to_cpu(context->rx->command_execute_resp.client_command);
> + resp_len = le32_to_cpu(context->rx->command_execute_resp.response_length);
> +
> + sahara_command_execute_data(context, client_cmd);
> +
> + if (client_cmd == SAHARA_EXEC_CMD_GET_COMMAND_ID_LIST) {
> + sahara_command_execute(context, SAHARA_EXEC_CMD_GET_TRAINING_DATA);
[Severity: High]
Can this concurrent reuse of the TX buffer cause data corruption or protocol
failure?
The driver queues context->tx[0] via sahara_command_execute_data() and
immediately overwrites the same buffer in sahara_command_execute() without
waiting for the MHI UL completion.
Additionally, pipelining these commands causes the ID list raw payload to
stream back to the host while receiving_training_data is false. Will
sahara_command_processing() then incorrectly interpret this raw data as a
Sahara command header?
> + return;
> + }
> +
> + if (client_cmd != SAHARA_EXEC_CMD_GET_TRAINING_DATA)
> + return;
> +
> + ct = sahara_cntrl_training_get(dev);
> + if (!ct) {
> + context->is_cmd_mode = false;
> + sahara_switch_mode_to_img_tx(context);
> + return;
> + }
> +
> + mutex_lock(&ct->lock);
> + kfree(ct->data);
> + ct->data = kzalloc(resp_len, GFP_KERNEL);
> + ct->size = resp_len;
> + ct->copied = 0;
> + ct->receiving = true;
> + mutex_unlock(&ct->lock);
> +
> + if (!ct->data) {
> + context->is_cmd_mode = false;
> + sahara_switch_mode_to_img_tx(context);
> + return;
> + }
> +
> + context->training_size = resp_len;
> + context->training_rcvd = 0;
> + context->receiving_training_data = true;
> +
> + remaining = resp_len;
> + for (i = 0; i < SAHARA_NUM_CMD_BUF && remaining; i++) {
[Severity: Medium]
If the device reports a response_length of 0, will this loop execute zero
times, queueing no RX buffers and hanging the channel since no completions
will arrive to trigger the state machine?
> + size_t pkt = min_t(size_t, remaining, SAHARA_PACKET_MAX_SIZE);
> +
> + ret = mhi_queue_buf(context->mhi_dev, DMA_FROM_DEVICE,
> + context->cmd_buff[i], pkt,
> + (remaining <= pkt) ? MHI_EOT : MHI_CHAIN);
> + if (ret)
> + break;
> +
> + remaining -= pkt;
> + }
> +
> + context->training_nbuf = i;
[Severity: High]
Does updating context->training_nbuf after queuing the buffers create a race
condition?
Since MHI queue operations are asynchronous, a completion could arrive
on another CPU and run sahara_mhi_dl_xfer_cb() before the loop finishes. If
training_nbuf is still 0, the callback might silently drop the valid
completion and stall the state machine.
[ ... ]
> @@ -899,15 +1155,71 @@ static void sahara_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result
> static void sahara_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
> {
> struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev);
> + struct sahara_cntrl_training_data *ct;
> + struct device *dev;
> + size_t copy;
> + int ret;
> + u32 i;
> +
> + if (mhi_result->transaction_status)
> + return;
> +
> + /*
> + * Raw training payload completions arrive for cmd_buff[] buffers.
> + * Do not schedule cmd_work for those.
> + */
> + if (context->is_cmd_mode && context->receiving_training_data &&
> + mhi_result->buf_addr != context->rx) {
> + dev = &context->mhi_dev->mhi_cntrl->mhi_dev->dev;
> + ct = sahara_cntrl_training_get(dev);
> + if (!ct)
> + return;
>
> - if (!mhi_result->transaction_status) {
> - context->rx_size = mhi_result->bytes_xferd;
> - if (context->is_mem_dump_mode)
> - schedule_work(&context->dump_work);
> - else
> - schedule_work(&context->fw_work);
> + for (i = 0; i < context->training_nbuf; i++) {
> + if (mhi_result->buf_addr == context->cmd_buff[i]) {
> + mutex_lock(&ct->lock);
[Severity: High]
Does acquiring a sleeping mutex here cause a scheduling while atomic bug?
The MHI downlink transfer callback is invoked from a tasklet context
(mhi_ev_task) holding a spinlock, which creates an atomic execution context.
> + copy = min_t(size_t, mhi_result->bytes_xferd,
> + ct->size - ct->copied);
> + memcpy((u8 *)ct->data + ct->copied,
> + mhi_result->buf_addr, copy);
> + ct->copied += copy;
> + mutex_unlock(&ct->lock);
> +
> + context->training_rcvd += copy;
> +
> + if (context->training_rcvd >= context->training_size) {
> + mutex_lock(&ct->lock);
> + ct->receiving = false;
> + mutex_unlock(&ct->lock);
> +
> + context->receiving_training_data = false;
> + context->is_cmd_mode = false;
> +
> + sahara_switch_mode_to_img_tx(context);
> + ret = mhi_queue_buf(context->mhi_dev,
> + DMA_FROM_DEVICE,
> + context->rx,
> + SAHARA_PACKET_MAX_SIZE,
> + MHI_EOT);
> + if (ret)
> + dev_err(&context->mhi_dev->dev,
> + "Unable to requeue rx buf %d\n", ret);
> + }
> + return;
[Severity: Medium]
If the payload reported by the device is larger than the initially queued
buffer capacity, does returning here starve the MHI channel?
Since additional RX buffers are not queued via mhi_queue_buf() for the
remaining data, the Sahara state machine could permanently hang waiting for
data that cannot arrive.
> + }
> + }
> + return;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261013-sahara_protocol_new_v2-v9-0-dc9f24357c4a@oss.qualcomm.com?part=6
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v9 2/7] bus: mhi: Move Sahara protocol driver under MHI host client drivers
2026-10-13 8:45 ` [PATCH v9 2/7] bus: mhi: Move Sahara protocol driver under MHI host client drivers Kishore Batta
@ 2026-10-13 9:00 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-13 9:00 UTC (permalink / raw)
To: Kishore Batta; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] qaic: leak of timesync and ras drivers on init failure
- [High] mhi: sahara: UAF due to missing read_data_work cancellation
- [Medium] bus: mhi: mhi_sahara module missing MODULE_DEVICE_TABLE for autoloading
- [Medium] kconfig: MHI_SAHARA missing select WANT_DEV_COREDUMP silently drops dumps
--
commit 1096a5d73941553ee9b70087bffc4c550e7d93dd
Author: Kishore Batta <kishore.batta@oss.qualcomm.com>
bus: mhi: Move Sahara protocol driver under MHI host client drivers
This commit moves the Sahara protocol driver from the QAIC accelerator
subsystem to the generic MHI host client drivers. It creates a dedicated
Kconfig and Makefile hierarchy for Sahara. The driver is also converted
to use module_mhi_driver() instead of manual register and unregister hooks.
> diff --git a/drivers/bus/mhi/host/clients/sahara/Kconfig b/drivers/bus/mhi/host/clients/sahara/Kconfig
> new file mode 100644
> index 0000000000000..f1fc7013a2dee
> --- /dev/null
> +++ b/drivers/bus/mhi/host/clients/sahara/Kconfig
> @@ -0,0 +1,15 @@
> +config MHI_SAHARA
> + tristate "Sahara protocol driver"
> + depends on MHI_BUS
[Severity: Medium]
Will this silently drop memory dumps on systems without WANT_DEV_COREDUMP
enabled?
Since WANT_DEV_COREDUMP is a hidden symbol (no prompt), users cannot manually
enable it. Because the newly extracted MHI_SAHARA driver advertises memory
dump retrieval and relies on dev_coredumpv(), omitting a select
WANT_DEV_COREDUMP statement means the devcoredump fallback stub might just
free the buffer without warning, leading to a silent loss of functionality.
> diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c
> index 0acf6cdd44f81..64e87dfaa5603 100644
> --- a/drivers/accel/qaic/qaic_drv.c
> +++ b/drivers/accel/qaic/qaic_drv.c
[ ... ]
> @@ -816,7 +809,6 @@ static int __init qaic_init(void)
>
> free_bootlog:
> qaic_bootlog_unregister();
> -free_mhi:
> mhi_driver_unregister(&qaic_mhi_driver);
> free_pci:
> pci_unregister_driver(&qaic_pci_driver);
[Severity: High]
Does this error path leak the timesync and ras MHI drivers?
If qaic_ssr_register() fails, the code jumps to free_bootlog. This cleanup
path unregisters the bootlog, MHI, and PCI drivers, but skips calling
qaic_timesync_deinit() and qaic_ras_unregister().
Because mhi_driver_register() adds these driver structures to the driver
core and sysfs, failing module initialization without unregistering them
leaves dangling pointers to the unloaded module's memory, which could
trigger a use-after-free crash if the bus core subsequently accesses them.
> diff --git a/drivers/accel/qaic/sahara.c b/drivers/bus/mhi/host/clients/sahara/sahara.c
> similarity index 99%
> rename from drivers/accel/qaic/sahara.c
> rename to drivers/bus/mhi/host/clients/sahara/sahara.c
> index c7c0b3eb4b65f..75c0cf6010e15 100644
> --- a/drivers/accel/qaic/sahara.c
> +++ b/drivers/bus/mhi/host/clients/sahara/sahara.c
[ ... ]
> @@ -922,13 +922,7 @@ static struct mhi_driver sahara_mhi_driver = {
> .name = "sahara",
> },
> };
> +module_mhi_driver(sahara_mhi_driver);
[Severity: Medium]
Will this driver fail to load automatically when the hardware is detected?
Because the driver is now extracted into an independent module, missing the
MODULE_DEVICE_TABLE macro for sahara_mhi_match_table means the match table
isn't exported to the module alias table. This prevents udev or modprobe
from automatically loading mhi_sahara when the QAIC_SAHARA channel is
detected.
[Severity: High]
Is there a use-after-free risk during device removal if read_data_work
remains pending?
When the device is removed or unbound via sysfs, sahara_mhi_remove() is
called. While fw_work and dump_work are cancelled, read_data_work is
completely omitted:
drivers/bus/mhi/host/clients/sahara/sahara.c:sahara_mhi_remove() {
struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev);
cancel_work_sync(&context->fw_work);
cancel_work_sync(&context->dump_work);
vfree(context->mem_dump);
sahara_release_image(context);
mhi_unprepare_from_transfer(mhi_dev);
}
If read_data_work was scheduled and remains pending, it will execute after
sahara_mhi_remove() returns and access the context structure, which has
already been freed.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261013-sahara_protocol_new_v2-v9-0-dc9f24357c4a@oss.qualcomm.com?part=2
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v9 5/7] bus: mhi: Load DDR training data using device serial number
2026-10-13 8:45 ` [PATCH v9 5/7] bus: mhi: Load DDR training data using device serial number Kishore Batta
@ 2026-10-13 9:00 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-13 9:00 UTC (permalink / raw)
To: Kishore Batta; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] mhi/sahara: UAF due to missing cancel_work_sync(read_data_work)
--
commit db3ce9303033a085ebf1250fe283d48253b7c50d
Author: Kishore Batta <kishore.batta@oss.qualcomm.com>
bus: mhi: Load DDR training data using device serial number
Extend the firmware loading logic for the DDR training image to first
attempt loading a per-device image dervied from the device serial number.
If the serial-specific image is not present, fall back to the existing
default image, preserving current behavior. This allows reuse of previously
generated DDR training data when available.
> diff --git a/drivers/bus/mhi/host/clients/sahara/sahara.c b/drivers/bus/mhi/host/clients/sahara/sahara.c
> index a4b3ef197b303..62b9c72e82680 100644
> --- a/drivers/bus/mhi/host/clients/sahara/sahara.c
> +++ b/drivers/bus/mhi/host/clients/sahara/sahara.c
> @@ -58,6 +58,7 @@
> #define SAHARA_RESET_LENGTH 0x8
> #define SAHARA_MEM_DEBUG64_LENGTH 0x18
> #define SAHARA_MEM_READ64_LENGTH 0x18
> +#define SAHARA_DDR_TRAINING_IMG_ID 34
>
> struct sahara_packet {
> __le32 cmd;
[ ... ]
[Severity: High]
Does driver teardown miss cancelling the read_data_work?
Looking at sahara_mhi_remove():
drivers/bus/mhi/host/clients/sahara/sahara.c:sahara_mhi_remove() {
...
cancel_work_sync(&context->fw_work);
cancel_work_sync(&context->dump_work);
vfree(context->mem_dump);
...
}
If the MHI device is unbound or hot-unplugged while a Sahara READ_DATA
streaming operation is active, read_data_work can be scheduled or already
running.
Since sahara_mhi_remove() omits cancelling read_data_work, when remove()
returns, the devres framework will automatically free the context structure
allocated via devm_kzalloc().
Can the uncancelled read_data_work then dereference the freed context
structure and cause a use-after-free?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261013-sahara_protocol_new_v2-v9-0-dc9f24357c4a@oss.qualcomm.com?part=5
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v9 4/7] bus: mhi: Add QDU100 Sahara variant and firmware fallback
2026-10-13 8:45 ` [PATCH v9 4/7] bus: mhi: Add QDU100 Sahara variant and firmware fallback Kishore Batta
@ 2026-10-13 9:01 ` sashiko-bot
0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-13 9:01 UTC (permalink / raw)
To: Kishore Batta; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] mhi: sahara: patch changes behavior for AIC100/AIC200 despite commit message claims
--
commit 95400c2cb3bcfef8a4adb0c664417375ff8137c7
Author: Kishore Batta <kishore.batta@oss.qualcomm.com>
bus: mhi: Add QDU100 Sahara variant and firmware fallback
Extend the probe time variant selection to match the SAHARA MHI channel and
associate it with the QDU100 firmware folder. Add a firmware lookup fallback
for cases where an image does not have an explicit entry in the device's
firmware table.
> This change only affects devices matched on the SAHARA MHI channel and
> does not change behavior for existing AIC100 and AIC200 devices.
Is this description accurate?
The qaic controller driver configures aic100_sahara_fw and aic200_sahara_fw to
populate the fw_folder field as "aic100" and "aic200".
With the new fallback logic in sahara_find_image(), if an existing AIC device
requests an unknown image ID, it appears it will now trigger the new fallback
path and perform synchronous VFS lookups for "qcom/aic.../<id>", whereas
previously it would immediately fail with -EINVAL.
> diff --git a/drivers/bus/mhi/host/clients/sahara/sahara.c b/drivers/bus/mhi/host/clients/sahara/sahara.c
> index ce4fcbd40baa1..a4b3ef197b303 100644
> --- a/drivers/bus/mhi/host/clients/sahara/sahara.c
> +++ b/drivers/bus/mhi/host/clients/sahara/sahara.c
[ ... ]
> @@ -200,8 +201,28 @@ static int sahara_find_image(struct sahara_context *context, u32 image_id)
> }
>
> if (image_id >= context->table_size || !context->image_table[image_id]) {
> - dev_err(&context->mhi_dev->dev, "request for unknown image: %d\n", image_id);
> - return -EINVAL;
> + if (!context->fw_folder) {
> + dev_err(&context->mhi_dev->dev,
> + "Request for unknown image: %u (no fw folder)\n", image_id);
> + return -EINVAL;
> + }
> +
> + fw_path = kasprintf(GFP_KERNEL, "qcom/%s/%u",
> + context->fw_folder, image_id);
> + if (!fw_path)
> + return -ENOMEM;
> +
> + ret = firmware_request_nowarn(&context->firmware,
> + fw_path,
> + &context->mhi_dev->dev);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[Severity: Medium]
Could this fallback path introduce a regression for AIC100 and AIC200
devices? It looks like those devices have a fw_folder configured, so they
would execute this filesystem lookup for unknown IDs rather than immediately
failing with -EINVAL as they did before this patch.
> + kfree(fw_path);
> + if (ret) {
> + dev_err(&context->mhi_dev->dev,
> + "request for unknown image: %d\n", image_id);
> + return -EINVAL;
> + }
> + context->active_image_id = image_id;
> + return 0;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261013-sahara_protocol_new_v2-v9-0-dc9f24357c4a@oss.qualcomm.com?part=4
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-10-13 9:01 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13 8:45 [PATCH v9 0/7] Qualcomm Sahara protocol enhancements Kishore Batta
2026-10-13 8:45 ` [PATCH v9 1/7] Add documentation for Sahara protocol Kishore Batta
2026-10-13 8:51 ` sashiko-bot
2026-10-13 8:45 ` [PATCH v9 2/7] bus: mhi: Move Sahara protocol driver under MHI host client drivers Kishore Batta
2026-10-13 9:00 ` sashiko-bot
2026-10-13 8:45 ` [PATCH v9 3/7] bus: mhi: Centralize Sahara firmware image table selection at probe time Kishore Batta
2026-10-13 8:45 ` [PATCH v9 4/7] bus: mhi: Add QDU100 Sahara variant and firmware fallback Kishore Batta
2026-10-13 9:01 ` sashiko-bot
2026-10-13 8:45 ` [PATCH v9 5/7] bus: mhi: Load DDR training data using device serial number Kishore Batta
2026-10-13 9:00 ` sashiko-bot
2026-10-13 8:45 ` [PATCH v9 6/7] bus: mhi: Capture DDR training data via command mode Kishore Batta
2026-10-13 8:59 ` sashiko-bot
2026-10-13 8:45 ` [PATCH v9 7/7] bus: mhi: Expose DDR training data via controller sysfs Kishore Batta
2026-10-13 8:58 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.