Merge "mhi: core: Add range check for channel id received in event ring"

This commit is contained in:
qctecmdr 2020-03-28 12:42:22 -07:00 • committed by Gerrit - the friendly Code Review server
commit d84e7b7049
6 changed files with 134 additions and 42 deletions

View file

@ -158,13 +158,14 @@ void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
MHI_LOG("BHIe programming for RDDM\n");
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_HIGH_OFFS,
mhi_cntrl->write_reg(mhi_cntrl, base, BHIE_RXVECADDR_HIGH_OFFS,
upper_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_LOW_OFFS,
mhi_cntrl->write_reg(mhi_cntrl, base, BHIE_RXVECADDR_LOW_OFFS,
lower_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS, mhi_buf->len);
mhi_cntrl->write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS,
mhi_buf->len);
sequence_id = prandom_u32() & BHIE_RXVECSTATUS_SEQNUM_BMSK;
if (unlikely(!sequence_id))
@ -234,7 +235,7 @@ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl)
/* Hardware reset; force device to enter rddm */
MHI_LOG(
"Did not enter RDDM, do a host req. reset\n");
mhi_write_reg(mhi_cntrl, mhi_cntrl->regs,
mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->regs,
MHI_SOC_RESET_REQ_OFFSET,
MHI_SOC_RESET_REQ);
udelay(delayus);
@ -310,13 +311,14 @@ static int mhi_fw_load_amss(struct mhi_controller *mhi_cntrl,
MHI_LOG("Starting BHIe Programming\n");
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_HIGH_OFFS,
mhi_cntrl->write_reg(mhi_cntrl, base, BHIE_TXVECADDR_HIGH_OFFS,
upper_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_LOW_OFFS,
mhi_cntrl->write_reg(mhi_cntrl, base, BHIE_TXVECADDR_LOW_OFFS,
lower_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECSIZE_OFFS, mhi_buf->len);
mhi_cntrl->write_reg(mhi_cntrl, base, BHIE_TXVECSIZE_OFFS,
mhi_buf->len);
mhi_cntrl->sequence_id = prandom_u32() & BHIE_TXVECSTATUS_SEQNUM_BMSK;
mhi_write_reg_field(mhi_cntrl, base, BHIE_TXVECDB_OFFS,
@ -374,14 +376,15 @@ static int mhi_fw_load_sbl(struct mhi_controller *mhi_cntrl,
goto invalid_pm_state;
}
mhi_write_reg(mhi_cntrl, base, BHI_STATUS, 0);
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH,
mhi_cntrl->write_reg(mhi_cntrl, base, BHI_STATUS, 0);
mhi_cntrl->write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH,
upper_32_bits(dma_addr));
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW,
mhi_cntrl->write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW,
lower_32_bits(dma_addr));
mhi_write_reg(mhi_cntrl, base, BHI_IMGSIZE, size);
mhi_cntrl->write_reg(mhi_cntrl, base, BHI_IMGSIZE, size);
mhi_cntrl->session_id = prandom_u32() & BHI_TXDB_SEQNUM_BMSK;
mhi_write_reg(mhi_cntrl, base, BHI_IMGTXDB, mhi_cntrl->session_id);
mhi_cntrl->write_reg(mhi_cntrl, base, BHI_IMGTXDB,
mhi_cntrl->session_id);
read_unlock_bh(pm_lock);
MHI_LOG("Waiting for image transfer completion\n");

View file

@ -683,7 +683,7 @@ static int mhi_init_bw_scale(struct mhi_controller *mhi_cntrl)
MHI_LOG("BW_CFG OFFSET:0x%x\n", bw_cfg_offset);
/* advertise host support */
mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, bw_cfg_offset,
mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->regs, bw_cfg_offset,
MHI_BW_SCALE_SETUP(er_index));
return 0;
@ -781,8 +781,8 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
/* setup wake db */
mhi_cntrl->wake_db = base + val + (8 * MHI_DEV_WAKE_DB);
mhi_write_reg(mhi_cntrl, mhi_cntrl->wake_db, 4, 0);
mhi_write_reg(mhi_cntrl, mhi_cntrl->wake_db, 0, 0);
mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->wake_db, 4, 0);
mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->wake_db, 0, 0);
mhi_cntrl->wake_set = false;
/* setup bw scale db */
@ -1405,6 +1405,8 @@ int of_register_mhi_controller(struct mhi_controller *mhi_cntrl)
mhi_cntrl->unmap_single = mhi_unmap_single_no_bb;
}
mhi_cntrl->write_reg = mhi_write_reg;
/* read the device info if possible */
if (mhi_cntrl->regs) {
ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs,

View file

@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */
/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */
#include <linux/msm_rtb.h>
@ -818,6 +818,8 @@ void mhi_destroy_timesync(struct mhi_controller *mhi_cntrl);
int mhi_create_sysfs(struct mhi_controller *mhi_cntrl);
void mhi_destroy_sysfs(struct mhi_controller *mhi_cntrl);
int mhi_early_notify_device(struct device *dev, void *data);
void mhi_write_reg_offload(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset, u32 val);
/* timesync log support */
static inline void mhi_timesync_log(struct mhi_controller *mhi_cntrl)
@ -911,6 +913,8 @@ void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
struct image_info *img_info);
int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
void mhi_reset_reg_write_q(struct mhi_controller *mhi_cntrl);
void mhi_force_reg_write(struct mhi_controller *mhi_cntrl);
/* isr handlers */
irqreturn_t mhi_msi_handlr(int irq_number, void *dev);
@ -918,22 +922,9 @@ irqreturn_t mhi_intvec_threaded_handlr(int irq_number, void *dev);
irqreturn_t mhi_intvec_handlr(int irq_number, void *dev);
void mhi_ev_task(unsigned long data);
#ifdef CONFIG_MHI_DEBUG
#define MHI_ASSERT(cond, msg) do { \
#define MHI_ASSERT(cond, fmt, ...) do { \
if (cond) \
panic(msg); \
panic(fmt); \
} while (0)
#else
#define MHI_ASSERT(cond, msg) do { \
if (cond) { \
MHI_ERR(msg); \
WARN_ON(cond); \
} \
} while (0)
#endif
#endif /* _MHI_INT_H */

View file

@ -89,6 +89,45 @@ int mhi_get_capability_offset(struct mhi_controller *mhi_cntrl,
return -ENXIO;
}
void mhi_force_reg_write(struct mhi_controller *mhi_cntrl)
{
if (mhi_cntrl->offload_wq)
flush_work(&mhi_cntrl->reg_write_work);
}
void mhi_reset_reg_write_q(struct mhi_controller *mhi_cntrl)
{
cancel_work_sync(&mhi_cntrl->reg_write_work);
memset(mhi_cntrl->reg_write_q, 0,
sizeof(struct reg_write_info) * REG_WRITE_QUEUE_LEN);
mhi_cntrl->read_idx = 0;
atomic_set(&mhi_cntrl->write_idx, -1);
}
static void mhi_reg_write_enqueue(struct mhi_controller *mhi_cntrl,
void __iomem *reg_addr, u32 val)
{
u32 q_index = atomic_inc_return(&mhi_cntrl->write_idx);
q_index = q_index & (REG_WRITE_QUEUE_LEN - 1);
MHI_ASSERT(mhi_cntrl->reg_write_q[q_index].valid, "queue full idx %d",
q_index);
mhi_cntrl->reg_write_q[q_index].reg_addr = reg_addr;
mhi_cntrl->reg_write_q[q_index].val = val;
mhi_cntrl->reg_write_q[q_index].valid = true;
}
void mhi_write_reg_offload(struct mhi_controller *mhi_cntrl,
void __iomem *base,
u32 offset,
u32 val)
{
mhi_reg_write_enqueue(mhi_cntrl, base + offset, val);
queue_work(mhi_cntrl->offload_wq, &mhi_cntrl->reg_write_work);
}
void mhi_write_reg(struct mhi_controller *mhi_cntrl,
void __iomem *base,
u32 offset,
@ -113,15 +152,15 @@ void mhi_write_reg_field(struct mhi_controller *mhi_cntrl,
tmp &= ~mask;
tmp |= (val << shift);
mhi_write_reg(mhi_cntrl, base, offset, tmp);
mhi_cntrl->write_reg(mhi_cntrl, base, offset, tmp);
}
void mhi_write_db(struct mhi_controller *mhi_cntrl,
void __iomem *db_addr,
dma_addr_t wp)
{
mhi_write_reg(mhi_cntrl, db_addr, 4, upper_32_bits(wp));
mhi_write_reg(mhi_cntrl, db_addr, 0, lower_32_bits(wp));
mhi_cntrl->write_reg(mhi_cntrl, db_addr, 4, upper_32_bits(wp));
mhi_cntrl->write_reg(mhi_cntrl, db_addr, 0, lower_32_bits(wp));
}
void mhi_db_brstmode(struct mhi_controller *mhi_cntrl,
@ -991,7 +1030,8 @@ 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;
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;
@ -1072,7 +1112,12 @@ static int parse_rsc_event(struct mhi_controller *mhi_cntrl,
xfer_len = MHI_TRE_GET_EV_LEN(event);
/* received out of bound cookie */
MHI_ASSERT(cookie >= buf_ring->len, "Invalid Cookie\n");
if (cookie >= buf_ring->len) {
MHI_ERR("cookie 0x%08x bufring_len %zu", cookie, buf_ring->len);
MHI_ERR("Processing Event:0x%llx 0x%08x 0x%08x\n",
event->ptr, event->dword[0], event->dword[1]);
panic("invalid cookie");
}
buf_info = buf_ring->base + cookie;
@ -1140,6 +1185,10 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
complete(&mhi_tsync->completion);
} else {
chan = MHI_TRE_GET_CMD_CHID(cmd_pkt);
if (chan >= mhi_cntrl->max_chan) {
MHI_ERR("invalid channel id %u\n", chan);
goto del_ring_el;
}
mhi_chan = &mhi_cntrl->mhi_chan[chan];
write_lock_bh(&mhi_chan->lock);
mhi_chan->ccs = MHI_TRE_GET_EV_CODE(tre);
@ -1147,6 +1196,7 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
write_unlock_bh(&mhi_chan->lock);
}
del_ring_el:
mhi_del_ring_element(mhi_cntrl, mhi_ring);
}
@ -1495,7 +1545,7 @@ int mhi_process_bw_scale_ev_ring(struct mhi_controller *mhi_cntrl,
read_lock_bh(&mhi_cntrl->pm_lock);
if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl)))
mhi_write_reg(mhi_cntrl, mhi_cntrl->bw_scale_db, 0,
mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->bw_scale_db, 0,
MHI_BW_SCALE_RESULT(result,
link_info.sequence_num));

View file

@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. */
/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */
#include <linux/debugfs.h>
#include <linux/delay.h>
@ -167,8 +167,8 @@ void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl,
mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 1);
} else {
mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
MHICTRL_MHISTATE_MASK, MHICTRL_MHISTATE_SHIFT, state);
mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->regs, MHICTRL,
(state << MHICTRL_MHISTATE_SHIFT));
}
}
@ -481,6 +481,12 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
wake_up_all(&mhi_cntrl->state_event);
/* offload register write if supported */
if (mhi_cntrl->offload_wq) {
mhi_reset_reg_write_q(mhi_cntrl);
mhi_cntrl->write_reg = mhi_write_reg_offload;
}
/* force MHI to be in M0 state before continuing */
ret = __mhi_device_get_sync(mhi_cntrl);
if (ret)
@ -556,6 +562,12 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
TO_MHI_STATE_STR(mhi_cntrl->dev_state),
to_mhi_pm_state_str(transition_state));
/* restore async write call back */
mhi_cntrl->write_reg = mhi_write_reg;
if (mhi_cntrl->offload_wq)
mhi_reset_reg_write_q(mhi_cntrl);
/* We must notify MHI control driver so it can clean up first */
if (transition_state == MHI_PM_SYS_ERR_PROCESS)
mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data,
@ -609,7 +621,7 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
* device cleares INTVEC as part of RESET processing,
* re-program it
*/
mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0);
mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0);
}
MHI_LOG("Waiting for all pending event ring processing to complete\n");
@ -932,7 +944,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
mhi_cntrl->bhie = mhi_cntrl->regs + val;
}
mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0);
mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0);
mhi_cntrl->pm_state = MHI_PM_POR;
mhi_cntrl->ee = MHI_EE_MAX;
current_ee = mhi_get_exec_env(mhi_cntrl);
@ -1001,6 +1013,8 @@ void mhi_control_error(struct mhi_controller *mhi_cntrl)
goto exit_control_error;
}
mhi_cntrl->dev_state = MHI_STATE_SYS_ERR;
/* notify waiters to bail out early since MHI has entered ERROR state */
wake_up_all(&mhi_cntrl->state_event);
@ -1146,6 +1160,9 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl)
write_unlock_irq(&mhi_cntrl->pm_lock);
MHI_LOG("Wait for M3 completion\n");
/* finish reg writes before D3 cold */
mhi_force_reg_write(mhi_cntrl);
ret = wait_event_timeout(mhi_cntrl->state_event,
mhi_cntrl->dev_state == MHI_STATE_M3 ||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),
@ -1272,6 +1289,9 @@ int mhi_pm_fast_suspend(struct mhi_controller *mhi_cntrl, bool notify_client)
mhi_cntrl->M3_FAST++;
write_unlock_irq(&mhi_cntrl->pm_lock);
/* finish reg writes before DRV hand-off to avoid noc err */
mhi_force_reg_write(mhi_cntrl);
/* now safe to check ctrl event ring */
tasklet_enable(&mhi_cntrl->mhi_event->task);
mhi_msi_handlr(0, mhi_cntrl->mhi_event);
@ -1477,6 +1497,8 @@ int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl)
mhi_trigger_resume(mhi_cntrl);
read_unlock_bh(&mhi_cntrl->pm_lock);
mhi_force_reg_write(mhi_cntrl);
ret = wait_event_timeout(mhi_cntrl->state_event,
mhi_cntrl->pm_state == MHI_PM_M0 ||
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state),

View file

@ -13,6 +13,8 @@ struct bhi_vec_entry;
struct mhi_timesync;
struct mhi_buf_info;
#define REG_WRITE_QUEUE_LEN 1024
/**
* enum MHI_CB - MHI callback
* @MHI_CB_IDLE: MHI entered idle state
@ -185,6 +187,19 @@ struct file_info {
u32 rem_seg_len;
};
/**
* struct reg_write_info - offload reg write info
* @reg_addr - register address
* @val - value to be written to register
* @chan - channel number
* @valid - entry is valid or not
*/
struct reg_write_info {
void __iomem *reg_addr;
u32 val;
bool valid;
};
/**
* struct mhi_controller - Master controller structure for external modem
* @dev: Device associated with this controller
@ -353,6 +368,8 @@ struct mhi_controller {
void (*tsync_log)(struct mhi_controller *mhi_cntrl, u64 remote_time);
int (*bw_scale)(struct mhi_controller *mhi_cntrl,
struct mhi_link_info *link_info);
void (*write_reg)(struct mhi_controller *mhi_cntrl, void __iomem *base,
u32 offset, u32 val);
/* channel to control DTR messaging */
struct mhi_device *dtr_dev;
@ -379,6 +396,13 @@ struct mhi_controller {
void *log_buf;
struct dentry *dentry;
struct dentry *parent;
/* for reg write offload */
struct workqueue_struct *offload_wq;
struct work_struct reg_write_work;
struct reg_write_info *reg_write_q;
atomic_t write_idx;
u32 read_idx;
};
/**