2020-05-09 02:28:39

by Bhaumik Bhatt

[permalink] [raw]
Subject: [PATCH v7 0/8] Bug fixes and improved logging in MHI

A set of patches for bug fixes and improved logging in mhi/core/boot.c.
Verified on x86 and arm64 platforms.

v7:
-Updated commit text for macro inclusion
-Updated channel ID bound checks
-Fixed non-uniform placement of function parameters to be within 80 characters
-Sent to correct Maintainer email ID

v6:
-Updated the MHI_RANDOM_U32_NONZERO to only give a random number upto the
supplied bitmask

v5:
-Updated the macro MHI_RANDOM_U32_NONZERO to take a bitmask as the input
parameter and output a non-zero value between 1 and U32_MAX

v4:
-Dropped the change: bus: mhi: core: WARN_ON for malformed vector table
-Updated bus: mhi: core: Read transfer length from an event properly to include
parse rsc events
-Use prandom_u32_max() instead of prandom_u32 to avoid if check in
bus: mhi: core: Ensure non-zero session or sequence ID values are used

v3:
-Fixed signed-off-by tags
-Add a refactor patch for MHI queue APIs
-Commit text fix in bus: mhi: core: Read transfer length from an event properly
-Fix channel ID range check for ctrl and data event rings processing

v2:
-Fix channel ID range check potential infinite loop
-Add appropriate signed-off-by tags

Bhaumik Bhatt (4):
bus: mhi: core: Handle firmware load using state worker
bus: mhi: core: Return appropriate error codes for AMSS load failure
bus: mhi: core: Improve debug logs for loading firmware
bus: mhi: core: Ensure non-zero session or sequence ID values are used

Hemant Kumar (4):
bus: mhi: core: Refactor mhi queue APIs
bus: mhi: core: Cache intmod from mhi event to mhi channel
bus: mhi: core: Add range check for channel id received in event ring
bus: mhi: core: Read transfer length from an event properly

drivers/bus/mhi/core/boot.c | 75 +++++++++----------
drivers/bus/mhi/core/init.c | 5 +-
drivers/bus/mhi/core/internal.h | 5 +-
drivers/bus/mhi/core/main.c | 156 +++++++++++++++++++++-------------------
drivers/bus/mhi/core/pm.c | 6 +-
include/linux/mhi.h | 2 -
6 files changed, 129 insertions(+), 120 deletions(-)

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2020-05-09 02:28:40

by Bhaumik Bhatt

[permalink] [raw]
Subject: [PATCH v7 1/8] bus: mhi: core: Refactor mhi queue APIs

From: Hemant Kumar <[email protected]>

Move all the common code to generate TRE from mhi_queue_buf,
mhi_queue_dma and mhi_queue_skb to mhi_gen_tre. This helps
to centralize the TRE generation code which makes any future
bug fixing easier to manage in these APIs.

Suggested-by: Jeffrey Hugo <[email protected]>
Signed-off-by: Hemant Kumar <[email protected]>
Signed-off-by: Bhaumik Bhatt <[email protected]>
Reviewed-by: Jeffrey Hugo <[email protected]>
---
drivers/bus/mhi/core/internal.h | 3 +-
drivers/bus/mhi/core/main.c | 107 +++++++++++++++++-----------------------
2 files changed, 47 insertions(+), 63 deletions(-)

diff --git a/drivers/bus/mhi/core/internal.h b/drivers/bus/mhi/core/internal.h
index 095d95b..40c47f9 100644
--- a/drivers/bus/mhi/core/internal.h
+++ b/drivers/bus/mhi/core/internal.h
@@ -670,8 +670,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
irqreturn_t mhi_intvec_handler(int irq_number, void *dev);

int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
- void *buf, void *cb, size_t buf_len, enum mhi_flags flags);
-
+ struct mhi_buf_info *info, enum mhi_flags flags);
int mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf_info);
int mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl,
diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
index c26eed0..605640c 100644
--- a/drivers/bus/mhi/core/main.c
+++ b/drivers/bus/mhi/core/main.c
@@ -919,9 +919,7 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan :
mhi_dev->dl_chan;
struct mhi_ring *tre_ring = &mhi_chan->tre_ring;
- struct mhi_ring *buf_ring = &mhi_chan->buf_ring;
- struct mhi_buf_info *buf_info;
- struct mhi_tre *mhi_tre;
+ struct mhi_buf_info buf_info = { };
int ret;

/* If MHI host pre-allocates buffers then client drivers cannot queue */
@@ -946,27 +944,15 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
/* Toggle wake to exit out of M2 */
mhi_cntrl->wake_toggle(mhi_cntrl);

- /* Generate the TRE */
- buf_info = buf_ring->wp;
-
- buf_info->v_addr = skb->data;
- buf_info->cb_buf = skb;
- buf_info->wp = tre_ring->wp;
- buf_info->dir = mhi_chan->dir;
- buf_info->len = len;
- ret = mhi_cntrl->map_single(mhi_cntrl, buf_info);
- if (ret)
- goto map_error;
-
- mhi_tre = tre_ring->wp;
-
- mhi_tre->ptr = MHI_TRE_DATA_PTR(buf_info->p_addr);
- mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(buf_info->len);
- mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(1, 1, 0, 0);
+ buf_info.v_addr = skb->data;
+ buf_info.cb_buf = skb;
+ buf_info.len = len;

- /* increment WP */
- mhi_add_ring_element(mhi_cntrl, tre_ring);
- mhi_add_ring_element(mhi_cntrl, buf_ring);
+ ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &buf_info, mflags);
+ if (unlikely(ret)) {
+ read_unlock_bh(&mhi_cntrl->pm_lock);
+ return ret;
+ }

if (mhi_chan->dir == DMA_TO_DEVICE)
atomic_inc(&mhi_cntrl->pending_pkts);
@@ -980,11 +966,6 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
read_unlock_bh(&mhi_cntrl->pm_lock);

return 0;
-
-map_error:
- read_unlock_bh(&mhi_cntrl->pm_lock);
-
- return ret;
}
EXPORT_SYMBOL_GPL(mhi_queue_skb);

@@ -996,9 +977,8 @@ int mhi_queue_dma(struct mhi_device *mhi_dev, enum dma_data_direction dir,
mhi_dev->dl_chan;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
struct mhi_ring *tre_ring = &mhi_chan->tre_ring;
- struct mhi_ring *buf_ring = &mhi_chan->buf_ring;
- struct mhi_buf_info *buf_info;
- struct mhi_tre *mhi_tre;
+ struct mhi_buf_info buf_info = { };
+ int ret;

/* If MHI host pre-allocates buffers then client drivers cannot queue */
if (mhi_chan->pre_alloc)
@@ -1025,25 +1005,16 @@ int mhi_queue_dma(struct mhi_device *mhi_dev, enum dma_data_direction dir,
/* Toggle wake to exit out of M2 */
mhi_cntrl->wake_toggle(mhi_cntrl);

- /* Generate the TRE */
- buf_info = buf_ring->wp;
- WARN_ON(buf_info->used);
- buf_info->p_addr = mhi_buf->dma_addr;
- buf_info->pre_mapped = true;
- buf_info->cb_buf = mhi_buf;
- buf_info->wp = tre_ring->wp;
- buf_info->dir = mhi_chan->dir;
- buf_info->len = len;
-
- mhi_tre = tre_ring->wp;
-
- mhi_tre->ptr = MHI_TRE_DATA_PTR(buf_info->p_addr);
- mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(buf_info->len);
- mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(1, 1, 0, 0);
+ buf_info.p_addr = mhi_buf->dma_addr;
+ buf_info.cb_buf = mhi_buf;
+ buf_info.pre_mapped = true;
+ buf_info.len = len;

- /* increment WP */
- mhi_add_ring_element(mhi_cntrl, tre_ring);
- mhi_add_ring_element(mhi_cntrl, buf_ring);
+ ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &buf_info, mflags);
+ if (unlikely(ret)) {
+ read_unlock_bh(&mhi_cntrl->pm_lock);
+ return ret;
+ }

if (mhi_chan->dir == DMA_TO_DEVICE)
atomic_inc(&mhi_cntrl->pending_pkts);
@@ -1061,7 +1032,7 @@ int mhi_queue_dma(struct mhi_device *mhi_dev, enum dma_data_direction dir,
EXPORT_SYMBOL_GPL(mhi_queue_dma);

int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
- void *buf, void *cb, size_t buf_len, enum mhi_flags flags)
+ struct mhi_buf_info *info, enum mhi_flags flags)
{
struct mhi_ring *buf_ring, *tre_ring;
struct mhi_tre *mhi_tre;
@@ -1073,15 +1044,22 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
tre_ring = &mhi_chan->tre_ring;

buf_info = buf_ring->wp;
- buf_info->v_addr = buf;
- buf_info->cb_buf = cb;
+ WARN_ON(buf_info->used);
+ buf_info->pre_mapped = info->pre_mapped;
+ if (info->pre_mapped)
+ buf_info->p_addr = info->p_addr;
+ else
+ buf_info->v_addr = info->v_addr;
+ buf_info->cb_buf = info->cb_buf;
buf_info->wp = tre_ring->wp;
buf_info->dir = mhi_chan->dir;
- buf_info->len = buf_len;
+ buf_info->len = info->len;

- ret = mhi_cntrl->map_single(mhi_cntrl, buf_info);
- if (ret)
- return ret;
+ if (!info->pre_mapped) {
+ ret = mhi_cntrl->map_single(mhi_cntrl, buf_info);
+ if (ret)
+ return ret;
+ }

eob = !!(flags & MHI_EOB);
eot = !!(flags & MHI_EOT);
@@ -1090,7 +1068,7 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,

mhi_tre = tre_ring->wp;
mhi_tre->ptr = MHI_TRE_DATA_PTR(buf_info->p_addr);
- mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(buf_len);
+ mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(info->len);
mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(bei, eot, eob, chain);

/* increment WP */
@@ -1107,6 +1085,7 @@ int mhi_queue_buf(struct mhi_device *mhi_dev, enum dma_data_direction dir,
struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan :
mhi_dev->dl_chan;
struct mhi_ring *tre_ring;
+ struct mhi_buf_info buf_info = { };
unsigned long flags;
int ret;

@@ -1122,7 +1101,11 @@ int mhi_queue_buf(struct mhi_device *mhi_dev, enum dma_data_direction dir,
if (mhi_is_ring_full(mhi_cntrl, tre_ring))
return -ENOMEM;

- ret = mhi_gen_tre(mhi_cntrl, mhi_chan, buf, buf, len, mflags);
+ buf_info.v_addr = buf;
+ buf_info.cb_buf = buf;
+ buf_info.len = len;
+
+ ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &buf_info, mflags);
if (unlikely(ret))
return ret;

@@ -1323,7 +1306,7 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,

while (nr_el--) {
void *buf;
-
+ struct mhi_buf_info info = { };
buf = kmalloc(len, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
@@ -1331,8 +1314,10 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
}

/* Prepare transfer descriptors */
- ret = mhi_gen_tre(mhi_cntrl, mhi_chan, buf, buf,
- len, MHI_EOT);
+ info.v_addr = buf;
+ info.cb_buf = buf;
+ info.len = len;
+ ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &info, MHI_EOT);
if (ret) {
kfree(buf);
goto error_pre_alloc;
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2020-05-09 02:28:49

by Bhaumik Bhatt

[permalink] [raw]
Subject: [PATCH v7 6/8] bus: mhi: core: Return appropriate error codes for AMSS load failure

When loading AMSS firmware using BHIe protocol, return -ETIMEDOUT if no
response is received within the timeout or return -EIO in case of a
protocol returned failure or an MHI error state.

Signed-off-by: Bhaumik Bhatt <[email protected]>
Reviewed-by: Jeffrey Hugo <[email protected]>
---
drivers/bus/mhi/core/boot.c | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/drivers/bus/mhi/core/boot.c b/drivers/bus/mhi/core/boot.c
index 17c636b..cf6dc5a 100644
--- a/drivers/bus/mhi/core/boot.c
+++ b/drivers/bus/mhi/core/boot.c
@@ -176,6 +176,7 @@ static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
void __iomem *base = mhi_cntrl->bhie;
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
u32 tx_status, sequence_id;
+ int ret;

read_lock_bh(pm_lock);
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
@@ -198,19 +199,19 @@ static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
read_unlock_bh(pm_lock);

/* Wait for the image download to complete */
- wait_event_timeout(mhi_cntrl->state_event,
- MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
- mhi_read_reg_field(mhi_cntrl, base,
- BHIE_TXVECSTATUS_OFFS,
- BHIE_TXVECSTATUS_STATUS_BMSK,
- BHIE_TXVECSTATUS_STATUS_SHFT,
- &tx_status) || tx_status,
- msecs_to_jiffies(mhi_cntrl->timeout_ms));
-
- if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
+ ret = wait_event_timeout(mhi_cntrl->state_event,
+ MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
+ mhi_read_reg_field(mhi_cntrl, base,
+ BHIE_TXVECSTATUS_OFFS,
+ BHIE_TXVECSTATUS_STATUS_BMSK,
+ BHIE_TXVECSTATUS_STATUS_SHFT,
+ &tx_status) || tx_status,
+ msecs_to_jiffies(mhi_cntrl->timeout_ms));
+ if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
+ tx_status != BHIE_TXVECSTATUS_STATUS_XFER_COMPL)
return -EIO;

- return (tx_status == BHIE_TXVECSTATUS_STATUS_XFER_COMPL) ? 0 : -EIO;
+ return (!ret) ? -ETIMEDOUT : 0;
}

static int mhi_fw_load_sbl(struct mhi_controller *mhi_cntrl,
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2020-05-09 02:29:01

by Bhaumik Bhatt

[permalink] [raw]
Subject: [PATCH v7 7/8] bus: mhi: core: Improve debug logs for loading firmware

Add log messages to track boot flow errors and timeouts in SBL or AMSS
firmware loading to aid in debug.

Signed-off-by: Bhaumik Bhatt <[email protected]>
Reviewed-by: Jeffrey Hugo <[email protected]>
---
drivers/bus/mhi/core/boot.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/drivers/bus/mhi/core/boot.c b/drivers/bus/mhi/core/boot.c
index cf6dc5a..80e4d76 100644
--- a/drivers/bus/mhi/core/boot.c
+++ b/drivers/bus/mhi/core/boot.c
@@ -121,7 +121,8 @@ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl)
ee = mhi_get_exec_env(mhi_cntrl);
}

- dev_dbg(dev, "Waiting for image download completion, current EE: %s\n",
+ dev_dbg(dev,
+ "Waiting for RDDM image download via BHIe, current EE:%s\n",
TO_MHI_EXEC_STR(ee));

while (retry--) {
@@ -152,11 +153,14 @@ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl)
int mhi_download_rddm_img(struct mhi_controller *mhi_cntrl, bool in_panic)
{
void __iomem *base = mhi_cntrl->bhie;
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
u32 rx_status;

if (in_panic)
return __mhi_download_rddm_in_panic(mhi_cntrl);

+ dev_dbg(dev, "Waiting for RDDM image download via BHIe\n");
+
/* Wait for the image download to complete */
wait_event_timeout(mhi_cntrl->state_event,
mhi_read_reg_field(mhi_cntrl, base,
@@ -174,6 +178,7 @@ static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
const struct mhi_buf *mhi_buf)
{
void __iomem *base = mhi_cntrl->bhie;
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
u32 tx_status, sequence_id;
int ret;
@@ -184,6 +189,7 @@ static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
return -EIO;
}

+ dev_dbg(dev, "Starting AMSS download via BHIe\n");
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_HIGH_OFFS,
upper_32_bits(mhi_buf->dma_addr));

@@ -435,7 +441,12 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
release_firmware(firmware);

/* Error or in EDL mode, we're done */
- if (ret || mhi_cntrl->ee == MHI_EE_EDL)
+ if (ret) {
+ dev_err(dev, "MHI did not load SBL, ret:%d\n", ret);
+ return;
+ }
+
+ if (mhi_cntrl->ee == MHI_EE_EDL)
return;

write_lock_irq(&mhi_cntrl->pm_lock);
@@ -463,8 +474,10 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
if (!mhi_cntrl->fbc_download)
return;

- if (ret)
+ if (ret) {
+ dev_err(dev, "MHI did not enter READY state\n");
goto error_read;
+ }

/* Wait for the SBL event */
ret = wait_event_timeout(mhi_cntrl->state_event,
@@ -482,6 +495,8 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
ret = mhi_fw_load_amss(mhi_cntrl,
/* Vector table is the last entry */
&image_info->mhi_buf[image_info->entries - 1]);
+ if (ret)
+ dev_err(dev, "MHI did not load AMSS, ret:%d\n", ret);

release_firmware(firmware);

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2020-05-09 02:29:07

by Bhaumik Bhatt

[permalink] [raw]
Subject: [PATCH v7 2/8] bus: mhi: core: Cache intmod from mhi event to mhi channel

From: Hemant Kumar <[email protected]>

Driver is using zero initialized intmod value from mhi channel when
configuring TRE for bei field. This prevents interrupt moderation to
take effect in case it is supported by an event ring. Fix this by
copying intmod value from associated event ring to mhi channel upon
registering mhi controller.

Signed-off-by: Hemant Kumar <[email protected]>
Signed-off-by: Bhaumik Bhatt <[email protected]>
Reviewed-by: Jeffrey Hugo <[email protected]>
---
drivers/bus/mhi/core/init.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/drivers/bus/mhi/core/init.c b/drivers/bus/mhi/core/init.c
index eb2ab05..1a93d24 100644
--- a/drivers/bus/mhi/core/init.c
+++ b/drivers/bus/mhi/core/init.c
@@ -863,6 +863,10 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
mutex_init(&mhi_chan->mutex);
init_completion(&mhi_chan->completion);
rwlock_init(&mhi_chan->lock);
+
+ /* used in setting bei field of TRE */
+ mhi_event = &mhi_cntrl->mhi_event[mhi_chan->er_index];
+ mhi_chan->intmod = mhi_event->intmod;
}

if (mhi_cntrl->bounce_buf) {
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2020-05-09 02:30:30

by Bhaumik Bhatt

[permalink] [raw]
Subject: [PATCH v7 8/8] bus: mhi: core: Ensure non-zero session or sequence ID values are used

While writing any sequence or session identifiers, it is possible that
the host could write a zero value, whereas only non-zero values should
be supported writes to those registers. Ensure that the host does not
write a non-zero value for them and also log them in debug messages. A
macro is introduced to simplify this check and the existing checks are
also converted to use this macro.

Signed-off-by: Bhaumik Bhatt <[email protected]>
Reviewed-by: Jeffrey Hugo <[email protected]>
---
drivers/bus/mhi/core/boot.c | 15 +++++++--------
drivers/bus/mhi/core/internal.h | 1 +
2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/bus/mhi/core/boot.c b/drivers/bus/mhi/core/boot.c
index 80e4d76..0b38014 100644
--- a/drivers/bus/mhi/core/boot.c
+++ b/drivers/bus/mhi/core/boot.c
@@ -43,10 +43,7 @@ void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
lower_32_bits(mhi_buf->dma_addr));

mhi_write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS, mhi_buf->len);
- sequence_id = prandom_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK;
-
- if (unlikely(!sequence_id))
- sequence_id = 1;
+ sequence_id = MHI_RANDOM_U32_NONZERO(BHIE_RXVECSTATUS_SEQNUM_BMSK);

mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS,
BHIE_RXVECDB_SEQNUM_BMSK, BHIE_RXVECDB_SEQNUM_SHFT,
@@ -189,7 +186,9 @@ static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
return -EIO;
}

- dev_dbg(dev, "Starting AMSS download via BHIe\n");
+ sequence_id = MHI_RANDOM_U32_NONZERO(BHIE_TXVECSTATUS_SEQNUM_BMSK);
+ dev_dbg(dev, "Starting AMSS download via BHIe. Sequence ID:%u\n",
+ sequence_id);
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_HIGH_OFFS,
upper_32_bits(mhi_buf->dma_addr));

@@ -198,7 +197,6 @@ static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,

mhi_write_reg(mhi_cntrl, base, BHIE_TXVECSIZE_OFFS, mhi_buf->len);

- sequence_id = prandom_u32() & BHIE_TXVECSTATUS_SEQNUM_BMSK;
mhi_write_reg_field(mhi_cntrl, base, BHIE_TXVECDB_OFFS,
BHIE_TXVECDB_SEQNUM_BMSK, BHIE_TXVECDB_SEQNUM_SHFT,
sequence_id);
@@ -246,14 +244,15 @@ static int mhi_fw_load_sbl(struct mhi_controller *mhi_cntrl,
goto invalid_pm_state;
}

- dev_dbg(dev, "Starting SBL download via BHI\n");
+ session_id = MHI_RANDOM_U32_NONZERO(BHI_TXDB_SEQNUM_BMSK);
+ dev_dbg(dev, "Starting SBL download via BHI. Session ID:%u\n",
+ session_id);
mhi_write_reg(mhi_cntrl, base, BHI_STATUS, 0);
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH,
upper_32_bits(dma_addr));
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW,
lower_32_bits(dma_addr));
mhi_write_reg(mhi_cntrl, base, BHI_IMGSIZE, size);
- session_id = prandom_u32() & BHI_TXDB_SEQNUM_BMSK;
mhi_write_reg(mhi_cntrl, base, BHI_IMGTXDB, session_id);
read_unlock_bh(pm_lock);

diff --git a/drivers/bus/mhi/core/internal.h b/drivers/bus/mhi/core/internal.h
index 0965ca3..80b32c2 100644
--- a/drivers/bus/mhi/core/internal.h
+++ b/drivers/bus/mhi/core/internal.h
@@ -452,6 +452,7 @@ enum mhi_pm_state {
#define PRIMARY_CMD_RING 0
#define MHI_DEV_WAKE_DB 127
#define MHI_MAX_MTU 0xffff
+#define MHI_RANDOM_U32_NONZERO(bmsk) (prandom_u32_max(bmsk) + 1)

enum mhi_er_type {
MHI_ER_TYPE_INVALID = 0x0,
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2020-05-09 02:31:34

by Bhaumik Bhatt

[permalink] [raw]
Subject: [PATCH v7 3/8] bus: mhi: core: Add range check for channel id received in event ring

From: Hemant Kumar <[email protected]>

MHI data completion handler function reads channel id from event
ring element. Value is under the control of MHI devices and can be
any value between 0 and 255. In order to prevent out of bound access
add a bound check against the max channel supported by controller
and skip processing of that event ring element.

Signed-off-by: Hemant Kumar <[email protected]>
Signed-off-by: Bhaumik Bhatt <[email protected]>
Reviewed-by: Jeffrey Hugo <[email protected]>
---
drivers/bus/mhi/core/main.c | 40 +++++++++++++++++++++++++++++-----------
1 file changed, 29 insertions(+), 11 deletions(-)

diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
index 605640c..30798ec 100644
--- a/drivers/bus/mhi/core/main.c
+++ b/drivers/bus/mhi/core/main.c
@@ -775,9 +775,18 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
}
case MHI_PKT_TYPE_TX_EVENT:
chan = MHI_TRE_GET_EV_CHID(local_rp);
- mhi_chan = &mhi_cntrl->mhi_chan[chan];
- parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
- event_quota--;
+
+ WARN_ON(chan >= mhi_cntrl->max_chan);
+
+ /*
+ * Only process the event ring elements whose channel
+ * ID is within the maximum supported range.
+ */
+ if (chan < mhi_cntrl->max_chan) {
+ mhi_chan = &mhi_cntrl->mhi_chan[chan];
+ parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
+ event_quota--;
+ }
break;
default:
dev_err(dev, "Unhandled event type: %d\n", type);
@@ -820,14 +829,23 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp);

chan = MHI_TRE_GET_EV_CHID(local_rp);
- mhi_chan = &mhi_cntrl->mhi_chan[chan];
-
- if (likely(type == MHI_PKT_TYPE_TX_EVENT)) {
- parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
- event_quota--;
- } else if (type == MHI_PKT_TYPE_RSC_TX_EVENT) {
- parse_rsc_event(mhi_cntrl, local_rp, mhi_chan);
- event_quota--;
+
+ WARN_ON(chan >= mhi_cntrl->max_chan);
+
+ /*
+ * Only process the event ring elements whose channel
+ * ID is within the maximum supported range.
+ */
+ if (chan < mhi_cntrl->max_chan) {
+ mhi_chan = &mhi_cntrl->mhi_chan[chan];
+
+ if (likely(type == MHI_PKT_TYPE_TX_EVENT)) {
+ parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
+ event_quota--;
+ } else if (type == MHI_PKT_TYPE_RSC_TX_EVENT) {
+ parse_rsc_event(mhi_cntrl, local_rp, mhi_chan);
+ event_quota--;
+ }
}

mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring);
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2020-05-09 02:31:34

by Bhaumik Bhatt

[permalink] [raw]
Subject: [PATCH v7 4/8] bus: mhi: core: Read transfer length from an event properly

From: Hemant Kumar <[email protected]>

When MHI Driver receives an EOT event, it reads xfer_len from the
event in the last TRE. The value is under control of the MHI device
and never validated by Host MHI driver. The value should never be
larger than the real size of the buffer but a malicious device can
set the value 0xFFFF as maximum. This causes driver to memory
overflow (both read or write). Fix this issue by reading minimum of
transfer length from event and the buffer length provided.

Signed-off-by: Hemant Kumar <[email protected]>
Signed-off-by: Bhaumik Bhatt <[email protected]>
Reviewed-by: Jeffrey Hugo <[email protected]>
---
drivers/bus/mhi/core/main.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
index 30798ec..6a80666 100644
--- a/drivers/bus/mhi/core/main.c
+++ b/drivers/bus/mhi/core/main.c
@@ -514,7 +514,10 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
mhi_cntrl->unmap_single(mhi_cntrl, buf_info);

result.buf_addr = buf_info->cb_buf;
- result.bytes_xferd = xfer_len;
+
+ /* truncate to buf len if xfer_len is larger */
+ result.bytes_xferd =
+ min_t(u16, xfer_len, buf_info->len);
mhi_del_ring_element(mhi_cntrl, buf_ring);
mhi_del_ring_element(mhi_cntrl, tre_ring);
local_rp = tre_ring->rp;
@@ -598,7 +601,9 @@ static int parse_rsc_event(struct mhi_controller *mhi_cntrl,

result.transaction_status = (ev_code == MHI_EV_CC_OVERFLOW) ?
-EOVERFLOW : 0;
- result.bytes_xferd = xfer_len;
+
+ /* truncate to buf len if xfer_len is larger */
+ result.bytes_xferd = min_t(u16, xfer_len, buf_info->len);
result.buf_addr = buf_info->cb_buf;
result.dir = mhi_chan->dir;

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2020-05-09 02:31:39

by Bhaumik Bhatt

[permalink] [raw]
Subject: [PATCH v7 5/8] bus: mhi: core: Handle firmware load using state worker

Upon power up, driver queues firmware worker thread if the execution
environment is PBL. Firmware worker is blocked with a timeout until
state worker gets a chance to run and unblock firmware worker. An
endpoint power up failure can be seen if state worker gets a chance to
run after firmware worker has timed out. Remove this dependency and
handle firmware load directly using state worker thread.

Signed-off-by: Bhaumik Bhatt <[email protected]>
Reviewed-by: Jeffrey Hugo <[email protected]>
---
drivers/bus/mhi/core/boot.c | 18 +++---------------
drivers/bus/mhi/core/init.c | 1 -
drivers/bus/mhi/core/internal.h | 1 +
drivers/bus/mhi/core/pm.c | 6 +-----
include/linux/mhi.h | 2 --
5 files changed, 5 insertions(+), 23 deletions(-)

diff --git a/drivers/bus/mhi/core/boot.c b/drivers/bus/mhi/core/boot.c
index ebad5eb..17c636b 100644
--- a/drivers/bus/mhi/core/boot.c
+++ b/drivers/bus/mhi/core/boot.c
@@ -377,30 +377,18 @@ static void mhi_firmware_copy(struct mhi_controller *mhi_cntrl,
}
}

-void mhi_fw_load_worker(struct work_struct *work)
+void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
{
- struct mhi_controller *mhi_cntrl;
const struct firmware *firmware = NULL;
struct image_info *image_info;
- struct device *dev;
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
const char *fw_name;
void *buf;
dma_addr_t dma_addr;
size_t size;
int ret;

- mhi_cntrl = container_of(work, struct mhi_controller, fw_worker);
- dev = &mhi_cntrl->mhi_dev->dev;
-
- dev_dbg(dev, "Waiting for device to enter PBL from: %s\n",
- TO_MHI_EXEC_STR(mhi_cntrl->ee));
-
- ret = wait_event_timeout(mhi_cntrl->state_event,
- MHI_IN_PBL(mhi_cntrl->ee) ||
- MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
- msecs_to_jiffies(mhi_cntrl->timeout_ms));
-
- if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
+ if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
dev_err(dev, "Device MHI is not in valid state\n");
return;
}
diff --git a/drivers/bus/mhi/core/init.c b/drivers/bus/mhi/core/init.c
index 1a93d24..6882206 100644
--- a/drivers/bus/mhi/core/init.c
+++ b/drivers/bus/mhi/core/init.c
@@ -835,7 +835,6 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
spin_lock_init(&mhi_cntrl->wlock);
INIT_WORK(&mhi_cntrl->st_worker, mhi_pm_st_worker);
INIT_WORK(&mhi_cntrl->syserr_worker, mhi_pm_sys_err_worker);
- INIT_WORK(&mhi_cntrl->fw_worker, mhi_fw_load_worker);
init_waitqueue_head(&mhi_cntrl->state_event);

mhi_cmd = mhi_cntrl->mhi_cmd;
diff --git a/drivers/bus/mhi/core/internal.h b/drivers/bus/mhi/core/internal.h
index 40c47f9..0965ca3 100644
--- a/drivers/bus/mhi/core/internal.h
+++ b/drivers/bus/mhi/core/internal.h
@@ -627,6 +627,7 @@ void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl,
void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl);
void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
struct image_info *img_info);
+void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl);
int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
diff --git a/drivers/bus/mhi/core/pm.c b/drivers/bus/mhi/core/pm.c
index e7c8318..3cc238a 100644
--- a/drivers/bus/mhi/core/pm.c
+++ b/drivers/bus/mhi/core/pm.c
@@ -528,7 +528,6 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
dev_dbg(dev, "Waiting for all pending threads to complete\n");
wake_up_all(&mhi_cntrl->state_event);
flush_work(&mhi_cntrl->st_worker);
- flush_work(&mhi_cntrl->fw_worker);

dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
device_for_each_child(mhi_cntrl->cntrl_dev, NULL, mhi_destroy_device);
@@ -643,7 +642,7 @@ void mhi_pm_st_worker(struct work_struct *work)
mhi_cntrl->ee = mhi_get_exec_env(mhi_cntrl);
write_unlock_irq(&mhi_cntrl->pm_lock);
if (MHI_IN_PBL(mhi_cntrl->ee))
- wake_up_all(&mhi_cntrl->state_event);
+ mhi_fw_load_handler(mhi_cntrl);
break;
case DEV_ST_TRANSITION_SBL:
write_lock_irq(&mhi_cntrl->pm_lock);
@@ -976,9 +975,6 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
next_state = MHI_IN_PBL(current_ee) ?
DEV_ST_TRANSITION_PBL : DEV_ST_TRANSITION_READY;

- if (next_state == DEV_ST_TRANSITION_PBL)
- schedule_work(&mhi_cntrl->fw_worker);
-
mhi_queue_state_transition(mhi_cntrl, next_state);

mutex_unlock(&mhi_cntrl->pm_mutex);
diff --git a/include/linux/mhi.h b/include/linux/mhi.h
index e909b8f..2b20b9c 100644
--- a/include/linux/mhi.h
+++ b/include/linux/mhi.h
@@ -331,7 +331,6 @@ struct mhi_controller_config {
* @wlock: Lock for protecting device wakeup
* @mhi_link_info: Device bandwidth info
* @st_worker: State transition worker
- * @fw_worker: Firmware download worker
* @syserr_worker: System error worker
* @state_event: State change event
* @status_cb: CB function to notify power states of the device (required)
@@ -412,7 +411,6 @@ struct mhi_controller {
spinlock_t wlock;
struct mhi_link_info mhi_link_info;
struct work_struct st_worker;
- struct work_struct fw_worker;
struct work_struct syserr_worker;
wait_queue_head_t state_event;

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2020-05-09 05:48:44

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v7 1/8] bus: mhi: core: Refactor mhi queue APIs

On Fri, May 08, 2020 at 07:26:41PM -0700, Bhaumik Bhatt wrote:
> From: Hemant Kumar <[email protected]>
>
> Move all the common code to generate TRE from mhi_queue_buf,
> mhi_queue_dma and mhi_queue_skb to mhi_gen_tre. This helps
> to centralize the TRE generation code which makes any future
> bug fixing easier to manage in these APIs.
>
> Suggested-by: Jeffrey Hugo <[email protected]>
> Signed-off-by: Hemant Kumar <[email protected]>
> Signed-off-by: Bhaumik Bhatt <[email protected]>
> Reviewed-by: Jeffrey Hugo <[email protected]>

Reviewed-by: Manivannan Sadhasivam <[email protected]>

Thanks,
Mani

> ---
> drivers/bus/mhi/core/internal.h | 3 +-
> drivers/bus/mhi/core/main.c | 107 +++++++++++++++++-----------------------
> 2 files changed, 47 insertions(+), 63 deletions(-)
>
> diff --git a/drivers/bus/mhi/core/internal.h b/drivers/bus/mhi/core/internal.h
> index 095d95b..40c47f9 100644
> --- a/drivers/bus/mhi/core/internal.h
> +++ b/drivers/bus/mhi/core/internal.h
> @@ -670,8 +670,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
> irqreturn_t mhi_intvec_handler(int irq_number, void *dev);
>
> int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
> - void *buf, void *cb, size_t buf_len, enum mhi_flags flags);
> -
> + struct mhi_buf_info *info, enum mhi_flags flags);
> int mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl,
> struct mhi_buf_info *buf_info);
> int mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl,
> diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
> index c26eed0..605640c 100644
> --- a/drivers/bus/mhi/core/main.c
> +++ b/drivers/bus/mhi/core/main.c
> @@ -919,9 +919,7 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
> struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan :
> mhi_dev->dl_chan;
> struct mhi_ring *tre_ring = &mhi_chan->tre_ring;
> - struct mhi_ring *buf_ring = &mhi_chan->buf_ring;
> - struct mhi_buf_info *buf_info;
> - struct mhi_tre *mhi_tre;
> + struct mhi_buf_info buf_info = { };
> int ret;
>
> /* If MHI host pre-allocates buffers then client drivers cannot queue */
> @@ -946,27 +944,15 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
> /* Toggle wake to exit out of M2 */
> mhi_cntrl->wake_toggle(mhi_cntrl);
>
> - /* Generate the TRE */
> - buf_info = buf_ring->wp;
> -
> - buf_info->v_addr = skb->data;
> - buf_info->cb_buf = skb;
> - buf_info->wp = tre_ring->wp;
> - buf_info->dir = mhi_chan->dir;
> - buf_info->len = len;
> - ret = mhi_cntrl->map_single(mhi_cntrl, buf_info);
> - if (ret)
> - goto map_error;
> -
> - mhi_tre = tre_ring->wp;
> -
> - mhi_tre->ptr = MHI_TRE_DATA_PTR(buf_info->p_addr);
> - mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(buf_info->len);
> - mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(1, 1, 0, 0);
> + buf_info.v_addr = skb->data;
> + buf_info.cb_buf = skb;
> + buf_info.len = len;
>
> - /* increment WP */
> - mhi_add_ring_element(mhi_cntrl, tre_ring);
> - mhi_add_ring_element(mhi_cntrl, buf_ring);
> + ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &buf_info, mflags);
> + if (unlikely(ret)) {
> + read_unlock_bh(&mhi_cntrl->pm_lock);
> + return ret;
> + }
>
> if (mhi_chan->dir == DMA_TO_DEVICE)
> atomic_inc(&mhi_cntrl->pending_pkts);
> @@ -980,11 +966,6 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
> read_unlock_bh(&mhi_cntrl->pm_lock);
>
> return 0;
> -
> -map_error:
> - read_unlock_bh(&mhi_cntrl->pm_lock);
> -
> - return ret;
> }
> EXPORT_SYMBOL_GPL(mhi_queue_skb);
>
> @@ -996,9 +977,8 @@ int mhi_queue_dma(struct mhi_device *mhi_dev, enum dma_data_direction dir,
> mhi_dev->dl_chan;
> struct device *dev = &mhi_cntrl->mhi_dev->dev;
> struct mhi_ring *tre_ring = &mhi_chan->tre_ring;
> - struct mhi_ring *buf_ring = &mhi_chan->buf_ring;
> - struct mhi_buf_info *buf_info;
> - struct mhi_tre *mhi_tre;
> + struct mhi_buf_info buf_info = { };
> + int ret;
>
> /* If MHI host pre-allocates buffers then client drivers cannot queue */
> if (mhi_chan->pre_alloc)
> @@ -1025,25 +1005,16 @@ int mhi_queue_dma(struct mhi_device *mhi_dev, enum dma_data_direction dir,
> /* Toggle wake to exit out of M2 */
> mhi_cntrl->wake_toggle(mhi_cntrl);
>
> - /* Generate the TRE */
> - buf_info = buf_ring->wp;
> - WARN_ON(buf_info->used);
> - buf_info->p_addr = mhi_buf->dma_addr;
> - buf_info->pre_mapped = true;
> - buf_info->cb_buf = mhi_buf;
> - buf_info->wp = tre_ring->wp;
> - buf_info->dir = mhi_chan->dir;
> - buf_info->len = len;
> -
> - mhi_tre = tre_ring->wp;
> -
> - mhi_tre->ptr = MHI_TRE_DATA_PTR(buf_info->p_addr);
> - mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(buf_info->len);
> - mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(1, 1, 0, 0);
> + buf_info.p_addr = mhi_buf->dma_addr;
> + buf_info.cb_buf = mhi_buf;
> + buf_info.pre_mapped = true;
> + buf_info.len = len;
>
> - /* increment WP */
> - mhi_add_ring_element(mhi_cntrl, tre_ring);
> - mhi_add_ring_element(mhi_cntrl, buf_ring);
> + ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &buf_info, mflags);
> + if (unlikely(ret)) {
> + read_unlock_bh(&mhi_cntrl->pm_lock);
> + return ret;
> + }
>
> if (mhi_chan->dir == DMA_TO_DEVICE)
> atomic_inc(&mhi_cntrl->pending_pkts);
> @@ -1061,7 +1032,7 @@ int mhi_queue_dma(struct mhi_device *mhi_dev, enum dma_data_direction dir,
> EXPORT_SYMBOL_GPL(mhi_queue_dma);
>
> int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
> - void *buf, void *cb, size_t buf_len, enum mhi_flags flags)
> + struct mhi_buf_info *info, enum mhi_flags flags)
> {
> struct mhi_ring *buf_ring, *tre_ring;
> struct mhi_tre *mhi_tre;
> @@ -1073,15 +1044,22 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
> tre_ring = &mhi_chan->tre_ring;
>
> buf_info = buf_ring->wp;
> - buf_info->v_addr = buf;
> - buf_info->cb_buf = cb;
> + WARN_ON(buf_info->used);
> + buf_info->pre_mapped = info->pre_mapped;
> + if (info->pre_mapped)
> + buf_info->p_addr = info->p_addr;
> + else
> + buf_info->v_addr = info->v_addr;
> + buf_info->cb_buf = info->cb_buf;
> buf_info->wp = tre_ring->wp;
> buf_info->dir = mhi_chan->dir;
> - buf_info->len = buf_len;
> + buf_info->len = info->len;
>
> - ret = mhi_cntrl->map_single(mhi_cntrl, buf_info);
> - if (ret)
> - return ret;
> + if (!info->pre_mapped) {
> + ret = mhi_cntrl->map_single(mhi_cntrl, buf_info);
> + if (ret)
> + return ret;
> + }
>
> eob = !!(flags & MHI_EOB);
> eot = !!(flags & MHI_EOT);
> @@ -1090,7 +1068,7 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
>
> mhi_tre = tre_ring->wp;
> mhi_tre->ptr = MHI_TRE_DATA_PTR(buf_info->p_addr);
> - mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(buf_len);
> + mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(info->len);
> mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(bei, eot, eob, chain);
>
> /* increment WP */
> @@ -1107,6 +1085,7 @@ int mhi_queue_buf(struct mhi_device *mhi_dev, enum dma_data_direction dir,
> struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan :
> mhi_dev->dl_chan;
> struct mhi_ring *tre_ring;
> + struct mhi_buf_info buf_info = { };
> unsigned long flags;
> int ret;
>
> @@ -1122,7 +1101,11 @@ int mhi_queue_buf(struct mhi_device *mhi_dev, enum dma_data_direction dir,
> if (mhi_is_ring_full(mhi_cntrl, tre_ring))
> return -ENOMEM;
>
> - ret = mhi_gen_tre(mhi_cntrl, mhi_chan, buf, buf, len, mflags);
> + buf_info.v_addr = buf;
> + buf_info.cb_buf = buf;
> + buf_info.len = len;
> +
> + ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &buf_info, mflags);
> if (unlikely(ret))
> return ret;
>
> @@ -1323,7 +1306,7 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
>
> while (nr_el--) {
> void *buf;
> -
> + struct mhi_buf_info info = { };
> buf = kmalloc(len, GFP_KERNEL);
> if (!buf) {
> ret = -ENOMEM;
> @@ -1331,8 +1314,10 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
> }
>
> /* Prepare transfer descriptors */
> - ret = mhi_gen_tre(mhi_cntrl, mhi_chan, buf, buf,
> - len, MHI_EOT);
> + info.v_addr = buf;
> + info.cb_buf = buf;
> + info.len = len;
> + ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &info, MHI_EOT);
> if (ret) {
> kfree(buf);
> goto error_pre_alloc;
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project

2020-05-09 05:51:13

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v7 3/8] bus: mhi: core: Add range check for channel id received in event ring

On Fri, May 08, 2020 at 07:26:43PM -0700, Bhaumik Bhatt wrote:
> From: Hemant Kumar <[email protected]>
>
> MHI data completion handler function reads channel id from event
> ring element. Value is under the control of MHI devices and can be
> any value between 0 and 255. In order to prevent out of bound access
> add a bound check against the max channel supported by controller
> and skip processing of that event ring element.
>
> Signed-off-by: Hemant Kumar <[email protected]>
> Signed-off-by: Bhaumik Bhatt <[email protected]>
> Reviewed-by: Jeffrey Hugo <[email protected]>

Reviewed-by: Manivannan Sadhasivam <[email protected]>

Thanks,
Mani

> ---
> drivers/bus/mhi/core/main.c | 40 +++++++++++++++++++++++++++++-----------
> 1 file changed, 29 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
> index 605640c..30798ec 100644
> --- a/drivers/bus/mhi/core/main.c
> +++ b/drivers/bus/mhi/core/main.c
> @@ -775,9 +775,18 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
> }
> case MHI_PKT_TYPE_TX_EVENT:
> chan = MHI_TRE_GET_EV_CHID(local_rp);
> - mhi_chan = &mhi_cntrl->mhi_chan[chan];
> - parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
> - event_quota--;
> +
> + WARN_ON(chan >= mhi_cntrl->max_chan);
> +
> + /*
> + * Only process the event ring elements whose channel
> + * ID is within the maximum supported range.
> + */
> + if (chan < mhi_cntrl->max_chan) {
> + mhi_chan = &mhi_cntrl->mhi_chan[chan];
> + parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
> + event_quota--;
> + }
> break;
> default:
> dev_err(dev, "Unhandled event type: %d\n", type);
> @@ -820,14 +829,23 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
> enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp);
>
> chan = MHI_TRE_GET_EV_CHID(local_rp);
> - mhi_chan = &mhi_cntrl->mhi_chan[chan];
> -
> - if (likely(type == MHI_PKT_TYPE_TX_EVENT)) {
> - parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
> - event_quota--;
> - } else if (type == MHI_PKT_TYPE_RSC_TX_EVENT) {
> - parse_rsc_event(mhi_cntrl, local_rp, mhi_chan);
> - event_quota--;
> +
> + WARN_ON(chan >= mhi_cntrl->max_chan);
> +
> + /*
> + * Only process the event ring elements whose channel
> + * ID is within the maximum supported range.
> + */
> + if (chan < mhi_cntrl->max_chan) {
> + mhi_chan = &mhi_cntrl->mhi_chan[chan];
> +
> + if (likely(type == MHI_PKT_TYPE_TX_EVENT)) {
> + parse_xfer_event(mhi_cntrl, local_rp, mhi_chan);
> + event_quota--;
> + } else if (type == MHI_PKT_TYPE_RSC_TX_EVENT) {
> + parse_rsc_event(mhi_cntrl, local_rp, mhi_chan);
> + event_quota--;
> + }
> }
>
> mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring);
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project

2020-05-09 05:51:35

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v7 2/8] bus: mhi: core: Cache intmod from mhi event to mhi channel

On Fri, May 08, 2020 at 07:26:42PM -0700, Bhaumik Bhatt wrote:
> From: Hemant Kumar <[email protected]>
>
> Driver is using zero initialized intmod value from mhi channel when
> configuring TRE for bei field. This prevents interrupt moderation to
> take effect in case it is supported by an event ring. Fix this by
> copying intmod value from associated event ring to mhi channel upon
> registering mhi controller.
>
> Signed-off-by: Hemant Kumar <[email protected]>
> Signed-off-by: Bhaumik Bhatt <[email protected]>
> Reviewed-by: Jeffrey Hugo <[email protected]>

Reviewed-by: Manivannan Sadhasivam <[email protected]>

Thanks,
Mani

> ---
> drivers/bus/mhi/core/init.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/drivers/bus/mhi/core/init.c b/drivers/bus/mhi/core/init.c
> index eb2ab05..1a93d24 100644
> --- a/drivers/bus/mhi/core/init.c
> +++ b/drivers/bus/mhi/core/init.c
> @@ -863,6 +863,10 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
> mutex_init(&mhi_chan->mutex);
> init_completion(&mhi_chan->completion);
> rwlock_init(&mhi_chan->lock);
> +
> + /* used in setting bei field of TRE */
> + mhi_event = &mhi_cntrl->mhi_event[mhi_chan->er_index];
> + mhi_chan->intmod = mhi_event->intmod;
> }
>
> if (mhi_cntrl->bounce_buf) {
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project

2020-05-09 05:51:56

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v7 4/8] bus: mhi: core: Read transfer length from an event properly

On Fri, May 08, 2020 at 07:26:44PM -0700, Bhaumik Bhatt wrote:
> From: Hemant Kumar <[email protected]>
>
> When MHI Driver receives an EOT event, it reads xfer_len from the
> event in the last TRE. The value is under control of the MHI device
> and never validated by Host MHI driver. The value should never be
> larger than the real size of the buffer but a malicious device can
> set the value 0xFFFF as maximum. This causes driver to memory
> overflow (both read or write). Fix this issue by reading minimum of
> transfer length from event and the buffer length provided.
>
> Signed-off-by: Hemant Kumar <[email protected]>
> Signed-off-by: Bhaumik Bhatt <[email protected]>
> Reviewed-by: Jeffrey Hugo <[email protected]>

Reviewed-by: Manivannan Sadhasivam <[email protected]>

Thanks,
Mani

> ---
> drivers/bus/mhi/core/main.c | 9 +++++++--
> 1 file changed, 7 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c
> index 30798ec..6a80666 100644
> --- a/drivers/bus/mhi/core/main.c
> +++ b/drivers/bus/mhi/core/main.c
> @@ -514,7 +514,10 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
> mhi_cntrl->unmap_single(mhi_cntrl, buf_info);
>
> result.buf_addr = buf_info->cb_buf;
> - result.bytes_xferd = xfer_len;
> +
> + /* truncate to buf len if xfer_len is larger */
> + result.bytes_xferd =
> + min_t(u16, xfer_len, buf_info->len);
> mhi_del_ring_element(mhi_cntrl, buf_ring);
> mhi_del_ring_element(mhi_cntrl, tre_ring);
> local_rp = tre_ring->rp;
> @@ -598,7 +601,9 @@ static int parse_rsc_event(struct mhi_controller *mhi_cntrl,
>
> result.transaction_status = (ev_code == MHI_EV_CC_OVERFLOW) ?
> -EOVERFLOW : 0;
> - result.bytes_xferd = xfer_len;
> +
> + /* truncate to buf len if xfer_len is larger */
> + result.bytes_xferd = min_t(u16, xfer_len, buf_info->len);
> result.buf_addr = buf_info->cb_buf;
> result.dir = mhi_chan->dir;
>
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project

2020-05-09 05:52:40

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v7 7/8] bus: mhi: core: Improve debug logs for loading firmware

On Fri, May 08, 2020 at 07:26:47PM -0700, Bhaumik Bhatt wrote:
> Add log messages to track boot flow errors and timeouts in SBL or AMSS
> firmware loading to aid in debug.
>
> Signed-off-by: Bhaumik Bhatt <[email protected]>
> Reviewed-by: Jeffrey Hugo <[email protected]>

Reviewed-by: Manivannan Sadhasivam <[email protected]>

Thanks,
Mani

> ---
> drivers/bus/mhi/core/boot.c | 21 ++++++++++++++++++---
> 1 file changed, 18 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/bus/mhi/core/boot.c b/drivers/bus/mhi/core/boot.c
> index cf6dc5a..80e4d76 100644
> --- a/drivers/bus/mhi/core/boot.c
> +++ b/drivers/bus/mhi/core/boot.c
> @@ -121,7 +121,8 @@ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl)
> ee = mhi_get_exec_env(mhi_cntrl);
> }
>
> - dev_dbg(dev, "Waiting for image download completion, current EE: %s\n",
> + dev_dbg(dev,
> + "Waiting for RDDM image download via BHIe, current EE:%s\n",
> TO_MHI_EXEC_STR(ee));
>
> while (retry--) {
> @@ -152,11 +153,14 @@ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl)
> int mhi_download_rddm_img(struct mhi_controller *mhi_cntrl, bool in_panic)
> {
> void __iomem *base = mhi_cntrl->bhie;
> + struct device *dev = &mhi_cntrl->mhi_dev->dev;
> u32 rx_status;
>
> if (in_panic)
> return __mhi_download_rddm_in_panic(mhi_cntrl);
>
> + dev_dbg(dev, "Waiting for RDDM image download via BHIe\n");
> +
> /* Wait for the image download to complete */
> wait_event_timeout(mhi_cntrl->state_event,
> mhi_read_reg_field(mhi_cntrl, base,
> @@ -174,6 +178,7 @@ static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
> const struct mhi_buf *mhi_buf)
> {
> void __iomem *base = mhi_cntrl->bhie;
> + struct device *dev = &mhi_cntrl->mhi_dev->dev;
> rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
> u32 tx_status, sequence_id;
> int ret;
> @@ -184,6 +189,7 @@ static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
> return -EIO;
> }
>
> + dev_dbg(dev, "Starting AMSS download via BHIe\n");
> mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_HIGH_OFFS,
> upper_32_bits(mhi_buf->dma_addr));
>
> @@ -435,7 +441,12 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
> release_firmware(firmware);
>
> /* Error or in EDL mode, we're done */
> - if (ret || mhi_cntrl->ee == MHI_EE_EDL)
> + if (ret) {
> + dev_err(dev, "MHI did not load SBL, ret:%d\n", ret);
> + return;
> + }
> +
> + if (mhi_cntrl->ee == MHI_EE_EDL)
> return;
>
> write_lock_irq(&mhi_cntrl->pm_lock);
> @@ -463,8 +474,10 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
> if (!mhi_cntrl->fbc_download)
> return;
>
> - if (ret)
> + if (ret) {
> + dev_err(dev, "MHI did not enter READY state\n");
> goto error_read;
> + }
>
> /* Wait for the SBL event */
> ret = wait_event_timeout(mhi_cntrl->state_event,
> @@ -482,6 +495,8 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
> ret = mhi_fw_load_amss(mhi_cntrl,
> /* Vector table is the last entry */
> &image_info->mhi_buf[image_info->entries - 1]);
> + if (ret)
> + dev_err(dev, "MHI did not load AMSS, ret:%d\n", ret);
>
> release_firmware(firmware);
>
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project

2020-05-09 05:53:20

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v7 8/8] bus: mhi: core: Ensure non-zero session or sequence ID values are used

On Fri, May 08, 2020 at 07:26:48PM -0700, Bhaumik Bhatt wrote:
> While writing any sequence or session identifiers, it is possible that
> the host could write a zero value, whereas only non-zero values should
> be supported writes to those registers. Ensure that the host does not
> write a non-zero value for them and also log them in debug messages. A
> macro is introduced to simplify this check and the existing checks are
> also converted to use this macro.
>
> Signed-off-by: Bhaumik Bhatt <[email protected]>
> Reviewed-by: Jeffrey Hugo <[email protected]>

Reviewed-by: Manivannan Sadhasivam <[email protected]>

Thanks,
Mani

> ---
> drivers/bus/mhi/core/boot.c | 15 +++++++--------
> drivers/bus/mhi/core/internal.h | 1 +
> 2 files changed, 8 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/bus/mhi/core/boot.c b/drivers/bus/mhi/core/boot.c
> index 80e4d76..0b38014 100644
> --- a/drivers/bus/mhi/core/boot.c
> +++ b/drivers/bus/mhi/core/boot.c
> @@ -43,10 +43,7 @@ void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
> lower_32_bits(mhi_buf->dma_addr));
>
> mhi_write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS, mhi_buf->len);
> - sequence_id = prandom_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK;
> -
> - if (unlikely(!sequence_id))
> - sequence_id = 1;
> + sequence_id = MHI_RANDOM_U32_NONZERO(BHIE_RXVECSTATUS_SEQNUM_BMSK);
>
> mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS,
> BHIE_RXVECDB_SEQNUM_BMSK, BHIE_RXVECDB_SEQNUM_SHFT,
> @@ -189,7 +186,9 @@ static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
> return -EIO;
> }
>
> - dev_dbg(dev, "Starting AMSS download via BHIe\n");
> + sequence_id = MHI_RANDOM_U32_NONZERO(BHIE_TXVECSTATUS_SEQNUM_BMSK);
> + dev_dbg(dev, "Starting AMSS download via BHIe. Sequence ID:%u\n",
> + sequence_id);
> mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_HIGH_OFFS,
> upper_32_bits(mhi_buf->dma_addr));
>
> @@ -198,7 +197,6 @@ static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
>
> mhi_write_reg(mhi_cntrl, base, BHIE_TXVECSIZE_OFFS, mhi_buf->len);
>
> - sequence_id = prandom_u32() & BHIE_TXVECSTATUS_SEQNUM_BMSK;
> mhi_write_reg_field(mhi_cntrl, base, BHIE_TXVECDB_OFFS,
> BHIE_TXVECDB_SEQNUM_BMSK, BHIE_TXVECDB_SEQNUM_SHFT,
> sequence_id);
> @@ -246,14 +244,15 @@ static int mhi_fw_load_sbl(struct mhi_controller *mhi_cntrl,
> goto invalid_pm_state;
> }
>
> - dev_dbg(dev, "Starting SBL download via BHI\n");
> + session_id = MHI_RANDOM_U32_NONZERO(BHI_TXDB_SEQNUM_BMSK);
> + dev_dbg(dev, "Starting SBL download via BHI. Session ID:%u\n",
> + session_id);
> mhi_write_reg(mhi_cntrl, base, BHI_STATUS, 0);
> mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH,
> upper_32_bits(dma_addr));
> mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW,
> lower_32_bits(dma_addr));
> mhi_write_reg(mhi_cntrl, base, BHI_IMGSIZE, size);
> - session_id = prandom_u32() & BHI_TXDB_SEQNUM_BMSK;
> mhi_write_reg(mhi_cntrl, base, BHI_IMGTXDB, session_id);
> read_unlock_bh(pm_lock);
>
> diff --git a/drivers/bus/mhi/core/internal.h b/drivers/bus/mhi/core/internal.h
> index 0965ca3..80b32c2 100644
> --- a/drivers/bus/mhi/core/internal.h
> +++ b/drivers/bus/mhi/core/internal.h
> @@ -452,6 +452,7 @@ enum mhi_pm_state {
> #define PRIMARY_CMD_RING 0
> #define MHI_DEV_WAKE_DB 127
> #define MHI_MAX_MTU 0xffff
> +#define MHI_RANDOM_U32_NONZERO(bmsk) (prandom_u32_max(bmsk) + 1)
>
> enum mhi_er_type {
> MHI_ER_TYPE_INVALID = 0x0,
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project

2020-05-09 05:54:02

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v7 5/8] bus: mhi: core: Handle firmware load using state worker

On Fri, May 08, 2020 at 07:26:45PM -0700, Bhaumik Bhatt wrote:
> Upon power up, driver queues firmware worker thread if the execution
> environment is PBL. Firmware worker is blocked with a timeout until
> state worker gets a chance to run and unblock firmware worker. An
> endpoint power up failure can be seen if state worker gets a chance to
> run after firmware worker has timed out. Remove this dependency and
> handle firmware load directly using state worker thread.
>
> Signed-off-by: Bhaumik Bhatt <[email protected]>
> Reviewed-by: Jeffrey Hugo <[email protected]>

Reviewed-by: Manivannan Sadhasivam <[email protected]>

Thanks,
Mani

> ---
> drivers/bus/mhi/core/boot.c | 18 +++---------------
> drivers/bus/mhi/core/init.c | 1 -
> drivers/bus/mhi/core/internal.h | 1 +
> drivers/bus/mhi/core/pm.c | 6 +-----
> include/linux/mhi.h | 2 --
> 5 files changed, 5 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/bus/mhi/core/boot.c b/drivers/bus/mhi/core/boot.c
> index ebad5eb..17c636b 100644
> --- a/drivers/bus/mhi/core/boot.c
> +++ b/drivers/bus/mhi/core/boot.c
> @@ -377,30 +377,18 @@ static void mhi_firmware_copy(struct mhi_controller *mhi_cntrl,
> }
> }
>
> -void mhi_fw_load_worker(struct work_struct *work)
> +void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
> {
> - struct mhi_controller *mhi_cntrl;
> const struct firmware *firmware = NULL;
> struct image_info *image_info;
> - struct device *dev;
> + struct device *dev = &mhi_cntrl->mhi_dev->dev;
> const char *fw_name;
> void *buf;
> dma_addr_t dma_addr;
> size_t size;
> int ret;
>
> - mhi_cntrl = container_of(work, struct mhi_controller, fw_worker);
> - dev = &mhi_cntrl->mhi_dev->dev;
> -
> - dev_dbg(dev, "Waiting for device to enter PBL from: %s\n",
> - TO_MHI_EXEC_STR(mhi_cntrl->ee));
> -
> - ret = wait_event_timeout(mhi_cntrl->state_event,
> - MHI_IN_PBL(mhi_cntrl->ee) ||
> - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
> - msecs_to_jiffies(mhi_cntrl->timeout_ms));
> -
> - if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
> + if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
> dev_err(dev, "Device MHI is not in valid state\n");
> return;
> }
> diff --git a/drivers/bus/mhi/core/init.c b/drivers/bus/mhi/core/init.c
> index 1a93d24..6882206 100644
> --- a/drivers/bus/mhi/core/init.c
> +++ b/drivers/bus/mhi/core/init.c
> @@ -835,7 +835,6 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
> spin_lock_init(&mhi_cntrl->wlock);
> INIT_WORK(&mhi_cntrl->st_worker, mhi_pm_st_worker);
> INIT_WORK(&mhi_cntrl->syserr_worker, mhi_pm_sys_err_worker);
> - INIT_WORK(&mhi_cntrl->fw_worker, mhi_fw_load_worker);
> init_waitqueue_head(&mhi_cntrl->state_event);
>
> mhi_cmd = mhi_cntrl->mhi_cmd;
> diff --git a/drivers/bus/mhi/core/internal.h b/drivers/bus/mhi/core/internal.h
> index 40c47f9..0965ca3 100644
> --- a/drivers/bus/mhi/core/internal.h
> +++ b/drivers/bus/mhi/core/internal.h
> @@ -627,6 +627,7 @@ void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl,
> void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl);
> void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
> struct image_info *img_info);
> +void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl);
> int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
> struct mhi_chan *mhi_chan);
> int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
> diff --git a/drivers/bus/mhi/core/pm.c b/drivers/bus/mhi/core/pm.c
> index e7c8318..3cc238a 100644
> --- a/drivers/bus/mhi/core/pm.c
> +++ b/drivers/bus/mhi/core/pm.c
> @@ -528,7 +528,6 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
> dev_dbg(dev, "Waiting for all pending threads to complete\n");
> wake_up_all(&mhi_cntrl->state_event);
> flush_work(&mhi_cntrl->st_worker);
> - flush_work(&mhi_cntrl->fw_worker);
>
> dev_dbg(dev, "Reset all active channels and remove MHI devices\n");
> device_for_each_child(mhi_cntrl->cntrl_dev, NULL, mhi_destroy_device);
> @@ -643,7 +642,7 @@ void mhi_pm_st_worker(struct work_struct *work)
> mhi_cntrl->ee = mhi_get_exec_env(mhi_cntrl);
> write_unlock_irq(&mhi_cntrl->pm_lock);
> if (MHI_IN_PBL(mhi_cntrl->ee))
> - wake_up_all(&mhi_cntrl->state_event);
> + mhi_fw_load_handler(mhi_cntrl);
> break;
> case DEV_ST_TRANSITION_SBL:
> write_lock_irq(&mhi_cntrl->pm_lock);
> @@ -976,9 +975,6 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
> next_state = MHI_IN_PBL(current_ee) ?
> DEV_ST_TRANSITION_PBL : DEV_ST_TRANSITION_READY;
>
> - if (next_state == DEV_ST_TRANSITION_PBL)
> - schedule_work(&mhi_cntrl->fw_worker);
> -
> mhi_queue_state_transition(mhi_cntrl, next_state);
>
> mutex_unlock(&mhi_cntrl->pm_mutex);
> diff --git a/include/linux/mhi.h b/include/linux/mhi.h
> index e909b8f..2b20b9c 100644
> --- a/include/linux/mhi.h
> +++ b/include/linux/mhi.h
> @@ -331,7 +331,6 @@ struct mhi_controller_config {
> * @wlock: Lock for protecting device wakeup
> * @mhi_link_info: Device bandwidth info
> * @st_worker: State transition worker
> - * @fw_worker: Firmware download worker
> * @syserr_worker: System error worker
> * @state_event: State change event
> * @status_cb: CB function to notify power states of the device (required)
> @@ -412,7 +411,6 @@ struct mhi_controller {
> spinlock_t wlock;
> struct mhi_link_info mhi_link_info;
> struct work_struct st_worker;
> - struct work_struct fw_worker;
> struct work_struct syserr_worker;
> wait_queue_head_t state_event;
>
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project

2020-05-09 05:54:30

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v7 6/8] bus: mhi: core: Return appropriate error codes for AMSS load failure

On Fri, May 08, 2020 at 07:26:46PM -0700, Bhaumik Bhatt wrote:
> When loading AMSS firmware using BHIe protocol, return -ETIMEDOUT if no
> response is received within the timeout or return -EIO in case of a
> protocol returned failure or an MHI error state.
>
> Signed-off-by: Bhaumik Bhatt <[email protected]>
> Reviewed-by: Jeffrey Hugo <[email protected]>

Reviewed-by: Manivannan Sadhasivam <[email protected]>

Thanks,
Mani

> ---
> drivers/bus/mhi/core/boot.c | 23 ++++++++++++-----------
> 1 file changed, 12 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/bus/mhi/core/boot.c b/drivers/bus/mhi/core/boot.c
> index 17c636b..cf6dc5a 100644
> --- a/drivers/bus/mhi/core/boot.c
> +++ b/drivers/bus/mhi/core/boot.c
> @@ -176,6 +176,7 @@ static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
> void __iomem *base = mhi_cntrl->bhie;
> rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
> u32 tx_status, sequence_id;
> + int ret;
>
> read_lock_bh(pm_lock);
> if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
> @@ -198,19 +199,19 @@ static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
> read_unlock_bh(pm_lock);
>
> /* Wait for the image download to complete */
> - wait_event_timeout(mhi_cntrl->state_event,
> - MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
> - mhi_read_reg_field(mhi_cntrl, base,
> - BHIE_TXVECSTATUS_OFFS,
> - BHIE_TXVECSTATUS_STATUS_BMSK,
> - BHIE_TXVECSTATUS_STATUS_SHFT,
> - &tx_status) || tx_status,
> - msecs_to_jiffies(mhi_cntrl->timeout_ms));
> -
> - if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
> + ret = wait_event_timeout(mhi_cntrl->state_event,
> + MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
> + mhi_read_reg_field(mhi_cntrl, base,
> + BHIE_TXVECSTATUS_OFFS,
> + BHIE_TXVECSTATUS_STATUS_BMSK,
> + BHIE_TXVECSTATUS_STATUS_SHFT,
> + &tx_status) || tx_status,
> + msecs_to_jiffies(mhi_cntrl->timeout_ms));
> + if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
> + tx_status != BHIE_TXVECSTATUS_STATUS_XFER_COMPL)
> return -EIO;
>
> - return (tx_status == BHIE_TXVECSTATUS_STATUS_XFER_COMPL) ? 0 : -EIO;
> + return (!ret) ? -ETIMEDOUT : 0;
> }
>
> static int mhi_fw_load_sbl(struct mhi_controller *mhi_cntrl,
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project

2020-05-09 08:34:18

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v7 0/8] Bug fixes and improved logging in MHI

Hi Bhaumik,

On Fri, May 08, 2020 at 07:26:40PM -0700, Bhaumik Bhatt wrote:
> A set of patches for bug fixes and improved logging in mhi/core/boot.c.
> Verified on x86 and arm64 platforms.
>

Series applied to mhi-next! I'll wait for one more -rc before sending the
final series to Greg for v5.8. If any other series gets reviewed by that point,
I'll club them together for the final one.

Thanks,
Mani

> v7:
> -Updated commit text for macro inclusion
> -Updated channel ID bound checks
> -Fixed non-uniform placement of function parameters to be within 80 characters
> -Sent to correct Maintainer email ID
>
> v6:
> -Updated the MHI_RANDOM_U32_NONZERO to only give a random number upto the
> supplied bitmask
>
> v5:
> -Updated the macro MHI_RANDOM_U32_NONZERO to take a bitmask as the input
> parameter and output a non-zero value between 1 and U32_MAX
>
> v4:
> -Dropped the change: bus: mhi: core: WARN_ON for malformed vector table
> -Updated bus: mhi: core: Read transfer length from an event properly to include
> parse rsc events
> -Use prandom_u32_max() instead of prandom_u32 to avoid if check in
> bus: mhi: core: Ensure non-zero session or sequence ID values are used
>
> v3:
> -Fixed signed-off-by tags
> -Add a refactor patch for MHI queue APIs
> -Commit text fix in bus: mhi: core: Read transfer length from an event properly
> -Fix channel ID range check for ctrl and data event rings processing
>
> v2:
> -Fix channel ID range check potential infinite loop
> -Add appropriate signed-off-by tags
>
> Bhaumik Bhatt (4):
> bus: mhi: core: Handle firmware load using state worker
> bus: mhi: core: Return appropriate error codes for AMSS load failure
> bus: mhi: core: Improve debug logs for loading firmware
> bus: mhi: core: Ensure non-zero session or sequence ID values are used
>
> Hemant Kumar (4):
> bus: mhi: core: Refactor mhi queue APIs
> bus: mhi: core: Cache intmod from mhi event to mhi channel
> bus: mhi: core: Add range check for channel id received in event ring
> bus: mhi: core: Read transfer length from an event properly
>
> drivers/bus/mhi/core/boot.c | 75 +++++++++----------
> drivers/bus/mhi/core/init.c | 5 +-
> drivers/bus/mhi/core/internal.h | 5 +-
> drivers/bus/mhi/core/main.c | 156 +++++++++++++++++++++-------------------
> drivers/bus/mhi/core/pm.c | 6 +-
> include/linux/mhi.h | 2 -
> 6 files changed, 129 insertions(+), 120 deletions(-)
>
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project