Add coredump support for Q6v5 Modem remoteproc
Subsystems (2)
v4 cover Add coredump support for Q6v5 Modem remoteproc
This patch series add coredump support for modem on SDM845, MSM8996 and MSM8916 SoCs. Modem requires the mba to be loaded before a coredump can be performed and this is achieved using a custom per segment dump function.
V4:
Addressed Bjorn's comments.
V3:
[bjorn]:replace prepare/unprepare ops with a more generalised per segment dump function
V2:
Introduce prepare/unprepare ops for rproc coredump
Sibi Sankar (5):
remoteproc: Introduce custom dump function for each remoteproc segment
remoteproc: Add mechanism for custom dump function assignment
remoteproc: qcom: q6v5-mss: Refactor mba load/unload sequence
remoteproc: qcom: q6v5-mss: Add custom dump function for modem
remoteproc: qcom: q6v5-mss: Register segments/dumpfn for coredump
drivers/remoteproc/qcom_q6v5_mss.c | 307 ++++++++++++++++++--------- drivers/remoteproc/remoteproc_core.c | 55 ++++- include/linux/remoteproc.h | 11 + 3 files changed, 268 insertions(+), 105 deletions(-)
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v4 1/5 remoteproc: Introduce custom dump function for each remoteproc segment
Introduce custom dump function and private data per remoteproc dump segment. The dump function is responsible for filling the device memory segment associated with coredump
--- drivers/remoteproc/remoteproc_core.c | 16 ++++++++++------ include/linux/remoteproc.h | 6 ++++++ 2 files changed, 16 insertions(+), 6 deletions(-)
drivers/remoteproc/remoteproc_core.c +10 -6
@@ -1183,14 +1183,18 @@ static void rproc_coredump(struct rproc *rproc) phdr->p_flags =PF_R |PF_W |PF_X ; phdr->p_align =0 ; - ptr =rproc_da_to_va (rproc, segment->da, segment->size); -if (!ptr) { -dev_err (&rproc->dev, +if (segment->dump) { + segment->dump (rproc, segment, data + offset); + }else { + ptr =rproc_da_to_va (rproc, segment->da, segment->size); +if (!ptr) { +dev_err (&rproc->dev,"invalid coredump segment (%pad, %zu)\n" , &segment->da, segment->size); -memset (data + offset,0xff , segment->size); - }else { -memcpy (data + offset, ptr, segment->size); +memset (data + offset,0xff , segment->size); + }else { +memcpy (data + offset, ptr, segment->size); + } } offset += phdr->p_filesz;
include/linux/remoteproc.h +6 -0
@@ -399,6 +399,9 @@ enum rproc_crash_type {* @node: list node related to the rproc segment list * @da: device address of the segment * @size: size of the segment +* @priv: private data associated with the dump_segment +* @dump: custom dump function to fill device memory segment associated +* with coredump */ struct rproc_dump_segment {struct list_head node;
@@ -406,6 +409,9 @@ struct rproc_dump_segment {dma_addr_t da;size_t size; +void *priv; +void (*dump)(struct rproc *rproc,struct rproc_dump_segment *segment, +void *dest);loff_t offset; };
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v4 2/5 remoteproc: Add mechanism for custom dump function assignment
In reply to Sibi SankarThis patch adds a mechanism for assigning each rproc dump segment with a custom dump function and private data. The dump function is to be called for each rproc segment during coredump if assigned.
--- drivers/remoteproc/remoteproc_core.c | 39 ++++++++++++++++++++++++++++ include/linux/remoteproc.h | 5 ++++ 2 files changed, 44 insertions(+)
drivers/remoteproc/remoteproc_core.c +39 -0
@@ -1121,6 +1121,45 @@ int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size) }EXPORT_SYMBOL (rproc_coredump_add_segment); +/** +* rproc_coredump_add_custom_segment() - add segment of device memory to +* coredump and extend it with custom +* dump function +* @rproc: handle of a remote processor +* @da: device address +* @size: size of segment +* @priv: private data +* @dumpfn: custom dump function called for each segment during coredump +* +* Add device memory to the list of segments to be included in the coredump +* and associate the segment with the given custom dump function and private +* data. +* +* Return: 0 on success, negative errno on error. +*/ +int rproc_coredump_add_custom_segment (struct rproc *rproc, +dma_addr_t da,size_t size,void *priv, +void (*dumpfn)(struct rproc *rproc, +struct rproc_dump_segment *segment, +void *dest)) +{ +struct rproc_dump_segment *segment; + + segment =kzalloc (sizeof (*segment),GFP_KERNEL ); +if (!segment) +return -ENOMEM ; + + segment->da = da; + segment->size = size; + segment->priv = priv; + segment->dump = dumpfn; + +list_add_tail (&segment->node, &rproc->dump_segments); + +return 0 ; +} +EXPORT_SYMBOL (rproc_coredump_add_custom_segment); +/** * rproc_coredump() - perform coredump * @rproc: rproc handle
include/linux/remoteproc.h +5 -0
@@ -563,6 +563,11 @@ int rproc_boot(struct rproc *rproc);void rproc_shutdown (struct rproc *rproc);void rproc_report_crash (struct rproc *rproc,enum rproc_crash_type type);int rproc_coredump_add_segment (struct rproc *rproc,dma_addr_t da,size_t size); +int rproc_coredump_add_custom_segment (struct rproc *rproc, +dma_addr_t da,size_t size,void *priv, +void (*dumpfn)(struct rproc *rproc, +struct rproc_dump_segment *segment, +void *dest));static inline struct rproc_vdev *vdev_to_rvdev (struct virtio_device *vdev) {
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v4 3/5 remoteproc: qcom: q6v5-mss: Refactor mba load/unload sequence
In reply to Sibi SankarRefactor re-useable parts of mba load/unload sequence into mba_load and mba_reclaim respectively. This is done in order to prevent code duplication for modem coredump, which requires the mba to be loaded before dumping the segments. No change in functionality is intended.
--- drivers/remoteproc/qcom_q6v5_mss.c | 239 +++++++++++++++++------------ 1 file changed, 140 insertions(+), 99 deletions(-)
drivers/remoteproc/qcom_q6v5_mss.c +140 -99
@@ -679,6 +679,143 @@ static bool q6v5_phdr_valid(const struct elf32_phdr *phdr)return true; } +static int q6v5_mba_load (struct q6v5 *qproc) +{ +int ret; +int xfermemop_ret; + + ret =q6v5_regulator_enable (qproc, qproc->proxy_regs, + qproc->proxy_reg_count); +if (ret) { +dev_err (qproc->dev,"failed to enable proxy supplies\n" ); +return ret; + } + + ret =q6v5_clk_enable (qproc->dev, qproc->proxy_clks, + qproc->proxy_clk_count); +if (ret) { +dev_err (qproc->dev,"failed to enable proxy clocks\n" ); + goto disable_proxy_reg; + } + + ret =q6v5_regulator_enable (qproc, qproc->active_regs, + qproc->active_reg_count); +if (ret) { +dev_err (qproc->dev,"failed to enable supplies\n" ); + goto disable_proxy_clk; + } + + ret =q6v5_clk_enable (qproc->dev, qproc->reset_clks, + qproc->reset_clk_count); +if (ret) { +dev_err (qproc->dev,"failed to enable reset clocks\n" ); + goto disable_vdd; + } + + ret =q6v5_reset_deassert (qproc); +if (ret) { +dev_err (qproc->dev,"failed to deassert mss restart\n" ); + goto disable_reset_clks; + } + + ret =q6v5_clk_enable (qproc->dev, qproc->active_clks, + qproc->active_clk_count); +if (ret) { +dev_err (qproc->dev,"failed to enable clocks\n" ); + goto assert_reset; + } + +/* Assign MBA image access in DDR to q6 */ + ret =q6v5_xfer_mem_ownership (qproc, &qproc->mba_perm, true, + qproc->mba_phys, qproc->mba_size); +if (ret) { +dev_err (qproc->dev, +"assigning Q6 access to mba memory failed: %d\n" , ret); + goto disable_active_clks; + } + +writel (qproc->mba_phys, qproc->rmb_base +RMB_MBA_IMAGE_REG ); + + ret =q6v5proc_reset (qproc); +if (ret) + goto reclaim_mba; + + ret =q6v5_rmb_mba_wait (qproc,0 ,5000 ); +if (ret == -ETIMEDOUT ) { +dev_err (qproc->dev,"MBA boot timed out\n" ); + goto halt_axi_ports; + }else if (ret !=RMB_MBA_XPU_UNLOCKED && + ret !=RMB_MBA_XPU_UNLOCKED_SCRIBBLED ) { +dev_err (qproc->dev,"MBA returned unexpected status %d\n" , ret); + ret = -EINVAL ; + goto halt_axi_ports; + } + +return 0 ; + +halt_axi_ports: +q6v5proc_halt_axi_port (qproc, qproc->halt_map, qproc->halt_q6); +q6v5proc_halt_axi_port (qproc, qproc->halt_map, qproc->halt_modem); +q6v5proc_halt_axi_port (qproc, qproc->halt_map, qproc->halt_nc); + +reclaim_mba: + xfermemop_ret =q6v5_xfer_mem_ownership (qproc, &qproc->mba_perm, false, + qproc->mba_phys, + qproc->mba_size); +if (xfermemop_ret) { +dev_err (qproc->dev, +"Failed to reclaim mba buffer, system may become unstable\n" ); + } + +disable_active_clks: +q6v5_clk_disable (qproc->dev, qproc->active_clks, + qproc->active_clk_count); +assert_reset: +q6v5_reset_assert (qproc); +disable_reset_clks: +q6v5_clk_disable (qproc->dev, qproc->reset_clks, + qproc->reset_clk_count); +disable_vdd: +q6v5_regulator_disable (qproc, qproc->active_regs, + qproc->active_reg_count); +disable_proxy_clk: +q6v5_clk_disable (qproc->dev, qproc->proxy_clks, + qproc->proxy_clk_count); +disable_proxy_reg: +q6v5_regulator_disable (qproc, qproc->proxy_regs, + qproc->proxy_reg_count); + +return ret; +} + +static void q6v5_mba_reclaim (struct q6v5 *qproc) +{ +int xfermemop_ret; + +q6v5proc_halt_axi_port (qproc, qproc->halt_map, qproc->halt_q6); +q6v5proc_halt_axi_port (qproc, qproc->halt_map, qproc->halt_modem); +q6v5proc_halt_axi_port (qproc, qproc->halt_map, qproc->halt_nc); + xfermemop_ret =q6v5_xfer_mem_ownership (qproc, &qproc->mba_perm, false, + qproc->mba_phys, + qproc->mba_size); +if (xfermemop_ret) { +dev_err (qproc->dev, +"Failed to reclaim mba buffer, system may become unstable\n" ); + } + +q6v5_clk_disable (qproc->dev, qproc->active_clks, + qproc->active_clk_count); +q6v5_reset_assert (qproc); +q6v5_clk_disable (qproc->dev, qproc->reset_clks, + qproc->reset_clk_count); +q6v5_regulator_disable (qproc, qproc->active_regs, + qproc->active_reg_count); +q6v5_clk_disable (qproc->dev, qproc->proxy_clks, + qproc->proxy_clk_count); +q6v5_regulator_disable (qproc, qproc->proxy_regs, + qproc->proxy_reg_count); +} +static int q6v5_mpss_load (struct q6v5 *qproc) {const struct elf32_phdr *phdrs;
@@ -803,72 +940,9 @@ static int q6v5_start(struct rproc *rproc)qcom_q6v5_prepare (&qproc->q6v5); - ret =q6v5_regulator_enable (qproc, qproc->proxy_regs, - qproc->proxy_reg_count); -if (ret) { -dev_err (qproc->dev,"failed to enable proxy supplies\n" ); - goto disable_irqs; - } - - ret =q6v5_clk_enable (qproc->dev, qproc->proxy_clks, - qproc->proxy_clk_count); -if (ret) { -dev_err (qproc->dev,"failed to enable proxy clocks\n" ); - goto disable_proxy_reg; - } - - ret =q6v5_regulator_enable (qproc, qproc->active_regs, - qproc->active_reg_count); -if (ret) { -dev_err (qproc->dev,"failed to enable supplies\n" ); - goto disable_proxy_clk; - } - - ret =q6v5_clk_enable (qproc->dev, qproc->reset_clks, - qproc->reset_clk_count); -if (ret) { -dev_err (qproc->dev,"failed to enable reset clocks\n" ); - goto disable_vdd; - } - - ret =q6v5_reset_deassert (qproc); -if (ret) { -dev_err (qproc->dev,"failed to deassert mss restart\n" ); - goto disable_reset_clks; - } - - ret =q6v5_clk_enable (qproc->dev, qproc->active_clks, - qproc->active_clk_count); -if (ret) { -dev_err (qproc->dev,"failed to enable clocks\n" ); - goto assert_reset; - } - -/* Assign MBA image access in DDR to q6 */ - ret =q6v5_xfer_mem_ownership (qproc, &qproc->mba_perm, true, - qproc->mba_phys, qproc->mba_size); -if (ret) { -dev_err (qproc->dev, -"assigning Q6 access to mba memory failed: %d\n" , ret); - goto disable_active_clks; - } - -writel (qproc->mba_phys, qproc->rmb_base +RMB_MBA_IMAGE_REG ); - - ret =q6v5proc_reset (qproc); + ret =q6v5_mba_load (qproc);if (ret) - goto reclaim_mba; - - ret =q6v5_rmb_mba_wait (qproc,0 ,5000 ); -if (ret == -ETIMEDOUT ) { -dev_err (qproc->dev,"MBA boot timed out\n" ); - goto halt_axi_ports; - }else if (ret !=RMB_MBA_XPU_UNLOCKED && - ret !=RMB_MBA_XPU_UNLOCKED_SCRIBBLED ) { -dev_err (qproc->dev,"MBA returned unexpected status %d\n" , ret); - ret = -EINVAL ; - goto halt_axi_ports; - } + goto disable_irqs;dev_info (qproc->dev,"MBA booted, loading mpss\n" );
@@ -897,40 +971,7 @@ static int q6v5_start(struct rproc *rproc) false, qproc->mpss_phys, qproc->mpss_size);WARN_ON (xfermemop_ret); - -halt_axi_ports: -q6v5proc_halt_axi_port (qproc, qproc->halt_map, qproc->halt_q6); -q6v5proc_halt_axi_port (qproc, qproc->halt_map, qproc->halt_modem); -q6v5proc_halt_axi_port (qproc, qproc->halt_map, qproc->halt_nc); - -reclaim_mba: - xfermemop_ret =q6v5_xfer_mem_ownership (qproc, &qproc->mba_perm, false, - qproc->mba_phys, - qproc->mba_size); -if (xfermemop_ret) { -dev_err (qproc->dev, -"Failed to reclaim mba buffer, system may become unstable\n" ); - } - -disable_active_clks: -q6v5_clk_disable (qproc->dev, qproc->active_clks, - qproc->active_clk_count); - -assert_reset: -q6v5_reset_assert (qproc); -disable_reset_clks: -q6v5_clk_disable (qproc->dev, qproc->reset_clks, - qproc->reset_clk_count); -disable_vdd: -q6v5_regulator_disable (qproc, qproc->active_regs, - qproc->active_reg_count); -disable_proxy_clk: -q6v5_clk_disable (qproc->dev, qproc->proxy_clks, - qproc->proxy_clk_count); -disable_proxy_reg: -q6v5_regulator_disable (qproc, qproc->proxy_regs, - qproc->proxy_reg_count); - +q6v5_mba_reclaim (qproc); disable_irqs:qcom_q6v5_unprepare (&qproc->q6v5);
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v4 4/5 remoteproc: qcom: q6v5-mss: Add custom dump function for modem
In reply to Sibi SankarThe per segment dump function is responsible for loading the mba before device memory segments associated with coredump can be populated and for cleaning up the resources post coredump.
--- drivers/remoteproc/qcom_q6v5_mss.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
drivers/remoteproc/qcom_q6v5_mss.c +25 -0
@@ -182,6 +182,7 @@ struct q6v5 {struct qcom_sysmon *sysmon;bool need_mem_protection;bool has_alt_reset; +unsigned long dump_segment_cnt;int mpss_perm;int mba_perm;int version;
@@ -932,6 +933,30 @@ static int q6v5_mpss_load(struct q6v5 *qproc)return ret <0 ? ret :0 ; } +static void qcom_q6v5_dump_segment (struct rproc *rproc, +struct rproc_dump_segment *segment, +void *dest) +{ +int ret =0 ; +struct q6v5 *qproc = rproc->priv; +unsigned long dump_cnt = (unsigned long )segment->priv; +void *ptr =rproc_da_to_va (rproc, segment->da, segment->size); + +/* Unlock mba before copying segments */ +if (!dump_cnt) + ret =q6v5_mba_load (qproc); + +if (!ptr || ret) +memset (dest,0xff , segment->size); +else +memcpy (dest, ptr, segment->size); + + dump_cnt++; +/* Reclaim mba after copying segments */ +if (dump_cnt == qproc->dump_segment_cnt) +q6v5_mba_reclaim (qproc); +} +static int q6v5_start (struct rproc *rproc) {struct q6v5 *qproc = (struct q6v5 *)rproc->priv;
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v4 5/5 remoteproc: qcom: q6v5-mss: Register segments/dumpfn for coredump
In reply to Sibi SankarRegister the MDT segments, custom dumpfn and private data with the remoteproc core dump functionality.
--- drivers/remoteproc/qcom_q6v5_mss.c | 43 ++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+)
drivers/remoteproc/qcom_q6v5_mss.c +43 -0
@@ -1065,10 +1065,53 @@ static void *q6v5_da_to_va(struct rproc *rproc, u64 da, int len)return qproc->mpss_region + offset; } +static int qcom_q6v5_register_dump_segments (struct rproc *rproc, +const struct firmware *mba_fw) +{ +const struct firmware *fw; +const struct elf32_phdr *phdrs; +const struct elf32_phdr *phdr; +const struct elf32_hdr *ehdr; +struct q6v5 *qproc = rproc->priv; +unsigned long segment_cnt =0 ; +int ret; +int i; + + ret =request_firmware (&fw,"modem.mdt" , qproc->dev); +if (ret <0 ) { +dev_err (qproc->dev,"unable to load modem.mdt\n" ); +return ret; + } + + ehdr = (struct elf32_hdr *)fw->data; + phdrs = (struct elf32_phdr *)(ehdr +1 ); + +for (i =0 ; i < ehdr->e_phnum; i++) { + phdr = &phdrs[i]; + +if (!q6v5_phdr_valid (phdr)) +continue ; + + ret =rproc_coredump_add_custom_segment (rproc, phdr->p_paddr, + phdr->p_memsz, + (void *)segment_cnt, + qcom_q6v5_dump_segment); +if (ret) +break ; + + segment_cnt++; + } + + qproc->dump_segment_cnt = segment_cnt; +release_firmware (fw); +return ret; +} +static const struct rproc_ops q6v5_ops = { .start = q6v5_start, .stop = q6v5_stop, .da_to_va = q6v5_da_to_va, + .parse_fw = qcom_q6v5_register_dump_segments, .load = q6v5_load, };
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project