Commit 92589cbd authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'rproc-v4.17' of git://github.com/andersson/remoteproc

Pull remoteproc updates from Bjorn Andersson:

 - add support for generating coredumps for remoteprocs using
   devcoredump

 - add the Qualcomm sysmon driver for intra-remoteproc crash handling

 - a number of fixes in Qualcomm and IMX drivers

* tag 'rproc-v4.17' of git://github.com/andersson/remoteproc:
  remoteproc: fix null pointer dereference on glink only platforms
  soc: qcom: qmi: add CONFIG_NET dependency
  remoteproc: imx_rproc: Slightly simplify code in 'imx_rproc_probe()'
  remoteproc: imx_rproc: Re-use existing error handling path in 'imx_rproc_probe()'
  remoteproc: imx_rproc: Fix an error handling path in 'imx_rproc_probe()'
  samples: Introduce Qualcomm QMI sample client
  remoteproc: qcom: Introduce sysmon
  remoteproc: Pass type of shutdown to subdev remove
  remoteproc: qcom: Register segments for core dump
  soc: qcom: mdt-loader: Return relocation base
  remoteproc: Rename "load_rsc_table" to "parse_fw"
  remoteproc: Add remote processor coredump support
  remoteproc: Remove null character write of shared mem
parents 9ab89c40 730b2ad8
......@@ -89,14 +89,14 @@ static int zap_shader_load_mdt(struct msm_gpu *gpu, const char *fwname)
*/
if (to_adreno_gpu(gpu)->fwloc == FW_LOCATION_LEGACY) {
ret = qcom_mdt_load(dev, fw, fwname, GPU_PAS_ID,
mem_region, mem_phys, mem_size);
mem_region, mem_phys, mem_size, NULL);
} else {
char newname[strlen("qcom/") + strlen(fwname) + 1];
sprintf(newname, "qcom/%s", fwname);
ret = qcom_mdt_load(dev, fw, newname, GPU_PAS_ID,
mem_region, mem_phys, mem_size);
mem_region, mem_phys, mem_size, NULL);
}
if (ret)
goto out;
......
......@@ -76,7 +76,7 @@ int venus_boot(struct device *dev, const char *fwname)
}
ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID, mem_va, mem_phys,
mem_size);
mem_size, NULL);
release_firmware(mdt);
......
......@@ -6,6 +6,7 @@ config REMOTEPROC
select CRC32
select FW_LOADER
select VIRTIO
select WANT_DEV_COREDUMP
help
Support for remote processors (such as DSP coprocessors). These
are mainly used on embedded systems.
......@@ -90,6 +91,7 @@ config QCOM_ADSP_PIL
depends on QCOM_SMEM
depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n)
depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
depends on QCOM_SYSMON || QCOM_SYSMON=n
select MFD_SYSCON
select QCOM_MDT_LOADER
select QCOM_RPROC_COMMON
......@@ -107,6 +109,7 @@ config QCOM_Q6V5_PIL
depends on QCOM_SMEM
depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n)
depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
depends on QCOM_SYSMON || QCOM_SYSMON=n
select MFD_SYSCON
select QCOM_RPROC_COMMON
select QCOM_SCM
......@@ -114,12 +117,28 @@ config QCOM_Q6V5_PIL
Say y here to support the Qualcomm Peripherial Image Loader for the
Hexagon V5 based remote processors.
config QCOM_SYSMON
tristate "Qualcomm sysmon driver"
depends on RPMSG
depends on ARCH_QCOM
depends on NET
select QCOM_QMI_HELPERS
help
The sysmon driver implements a sysmon QMI client and a handler for
the sys_mon SMD and GLINK channel, which are used for graceful
shutdown, retrieving failure information and propagating information
about other subsystems being shut down.
Say y here if your system runs firmware on any other subsystems, e.g.
modem or DSP.
config QCOM_WCNSS_PIL
tristate "Qualcomm WCNSS Peripheral Image Loader"
depends on OF && ARCH_QCOM
depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n)
depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
depends on QCOM_SMEM
depends on QCOM_SYSMON || QCOM_SYSMON=n
select QCOM_MDT_LOADER
select QCOM_RPROC_COMMON
select QCOM_SCM
......
......@@ -17,6 +17,7 @@ obj-$(CONFIG_KEYSTONE_REMOTEPROC) += keystone_remoteproc.o
obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o
obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o
obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o
obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o
obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o
qcom_wcnss_pil-y += qcom_wcnss.o
qcom_wcnss_pil-y += qcom_wcnss_iris.o
......
......@@ -333,14 +333,14 @@ static int imx_rproc_probe(struct platform_device *pdev)
/* set some other name then imx */
rproc = rproc_alloc(dev, "imx-rproc", &imx_rproc_ops,
NULL, sizeof(*priv));
if (!rproc) {
ret = -ENOMEM;
goto err;
}
if (!rproc)
return -ENOMEM;
dcfg = of_device_get_match_data(dev);
if (!dcfg)
return -EINVAL;
if (!dcfg) {
ret = -EINVAL;
goto err_put_rproc;
}
priv = rproc->priv;
priv->rproc = rproc;
......@@ -359,8 +359,8 @@ static int imx_rproc_probe(struct platform_device *pdev)
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk)) {
dev_err(dev, "Failed to get clock\n");
rproc_free(rproc);
return PTR_ERR(priv->clk);
ret = PTR_ERR(priv->clk);
goto err_put_rproc;
}
/*
......@@ -370,8 +370,7 @@ static int imx_rproc_probe(struct platform_device *pdev)
ret = clk_prepare_enable(priv->clk);
if (ret) {
dev_err(&rproc->dev, "Failed to enable clock\n");
rproc_free(rproc);
return ret;
goto err_put_rproc;
}
ret = rproc_add(rproc);
......@@ -380,13 +379,13 @@ static int imx_rproc_probe(struct platform_device *pdev)
goto err_put_clk;
}
return ret;
return 0;
err_put_clk:
clk_disable_unprepare(priv->clk);
err_put_rproc:
rproc_free(rproc);
err:
return ret;
}
......
......@@ -38,7 +38,10 @@ struct adsp_data {
const char *firmware_name;
int pas_id;
bool has_aggre2_clk;
const char *ssr_name;
const char *sysmon_name;
int ssctl_id;
};
struct qcom_adsp {
......@@ -75,6 +78,7 @@ struct qcom_adsp {
struct qcom_rproc_glink glink_subdev;
struct qcom_rproc_subdev smd_subdev;
struct qcom_rproc_ssr ssr_subdev;
struct qcom_sysmon *sysmon;
};
static int adsp_load(struct rproc *rproc, const struct firmware *fw)
......@@ -82,7 +86,9 @@ static int adsp_load(struct rproc *rproc, const struct firmware *fw)
struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id,
adsp->mem_region, adsp->mem_phys, adsp->mem_size);
adsp->mem_region, adsp->mem_phys, adsp->mem_size,
&adsp->mem_reloc);
}
static int adsp_start(struct rproc *rproc)
......@@ -177,6 +183,7 @@ static const struct rproc_ops adsp_ops = {
.start = adsp_start,
.stop = adsp_stop,
.da_to_va = adsp_da_to_va,
.parse_fw = qcom_register_dump_segments,
.load = adsp_load,
};
......@@ -201,9 +208,6 @@ static irqreturn_t adsp_fatal_interrupt(int irq, void *dev)
rproc_report_crash(adsp->rproc, RPROC_FATAL_ERROR);
if (!IS_ERR(msg))
msg[0] = '\0';
return IRQ_HANDLED;
}
......@@ -398,6 +402,9 @@ static int adsp_probe(struct platform_device *pdev)
qcom_add_glink_subdev(rproc, &adsp->glink_subdev);
qcom_add_smd_subdev(rproc, &adsp->smd_subdev);
qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name);
adsp->sysmon = qcom_add_sysmon_subdev(rproc,
desc->sysmon_name,
desc->ssctl_id);
ret = rproc_add(rproc);
if (ret)
......@@ -419,6 +426,7 @@ static int adsp_remove(struct platform_device *pdev)
rproc_del(adsp->rproc);
qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev);
qcom_remove_sysmon_subdev(adsp->sysmon);
qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev);
qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev);
rproc_free(adsp->rproc);
......@@ -432,6 +440,8 @@ static const struct adsp_data adsp_resource_init = {
.pas_id = 1,
.has_aggre2_clk = false,
.ssr_name = "lpass",
.sysmon_name = "adsp",
.ssctl_id = 0x14,
};
static const struct adsp_data slpi_resource_init = {
......@@ -440,6 +450,8 @@ static const struct adsp_data slpi_resource_init = {
.pas_id = 12,
.has_aggre2_clk = true,
.ssr_name = "dsps",
.sysmon_name = "slpi",
.ssctl_id = 0x16,
};
static const struct of_device_id adsp_of_match[] = {
......
......@@ -22,6 +22,7 @@
#include <linux/remoteproc.h>
#include <linux/rpmsg/qcom_glink.h>
#include <linux/rpmsg/qcom_smd.h>
#include <linux/soc/qcom/mdt_loader.h>
#include "remoteproc_internal.h"
#include "qcom_common.h"
......@@ -41,7 +42,7 @@ static int glink_subdev_probe(struct rproc_subdev *subdev)
return PTR_ERR_OR_ZERO(glink->edge);
}
static void glink_subdev_remove(struct rproc_subdev *subdev)
static void glink_subdev_remove(struct rproc_subdev *subdev, bool crashed)
{
struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
......@@ -74,11 +75,57 @@ EXPORT_SYMBOL_GPL(qcom_add_glink_subdev);
*/
void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink)
{
if (!glink->node)
return;
rproc_remove_subdev(rproc, &glink->subdev);
of_node_put(glink->node);
}
EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev);
/**
* qcom_register_dump_segments() - register segments for coredump
* @rproc: remoteproc handle
* @fw: firmware header
*
* Register all segments of the ELF in the remoteproc coredump segment list
*
* Return: 0 on success, negative errno on failure.
*/
int qcom_register_dump_segments(struct rproc *rproc,
const struct firmware *fw)
{
const struct elf32_phdr *phdrs;
const struct elf32_phdr *phdr;
const struct elf32_hdr *ehdr;
int ret;
int i;
ehdr = (struct elf32_hdr *)fw->data;
phdrs = (struct elf32_phdr *)(ehdr + 1);
for (i = 0; i < ehdr->e_phnum; i++) {
phdr = &phdrs[i];
if (phdr->p_type != PT_LOAD)
continue;
if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
continue;
if (!phdr->p_memsz)
continue;
ret = rproc_coredump_add_segment(rproc, phdr->p_paddr,
phdr->p_memsz);
if (ret)
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(qcom_register_dump_segments);
static int smd_subdev_probe(struct rproc_subdev *subdev)
{
struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
......@@ -88,7 +135,7 @@ static int smd_subdev_probe(struct rproc_subdev *subdev)
return PTR_ERR_OR_ZERO(smd->edge);
}
static void smd_subdev_remove(struct rproc_subdev *subdev)
static void smd_subdev_remove(struct rproc_subdev *subdev, bool crashed)
{
struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
......@@ -121,6 +168,9 @@ EXPORT_SYMBOL_GPL(qcom_add_smd_subdev);
*/
void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
{
if (!smd->node)
return;
rproc_remove_subdev(rproc, &smd->subdev);
of_node_put(smd->node);
}
......@@ -157,7 +207,7 @@ static int ssr_notify_start(struct rproc_subdev *subdev)
return 0;
}
static void ssr_notify_stop(struct rproc_subdev *subdev)
static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed)
{
struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
......
......@@ -4,6 +4,9 @@
#include <linux/remoteproc.h>
#include "remoteproc_internal.h"
#include <linux/soc/qcom/qmi.h>
struct qcom_sysmon;
struct qcom_rproc_glink {
struct rproc_subdev subdev;
......@@ -30,6 +33,8 @@ struct qcom_rproc_ssr {
void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink);
void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink);
int qcom_register_dump_segments(struct rproc *rproc, const struct firmware *fw);
void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd);
void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd);
......@@ -37,4 +42,22 @@ void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr,
const char *ssr_name);
void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr);
#if IS_ENABLED(CONFIG_QCOM_SYSMON)
struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
const char *name,
int ssctl_instance);
void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon);
#else
static inline struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
const char *name,
int ssctl_instance)
{
return NULL;
}
static inline void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon)
{
}
#endif
#endif
......@@ -168,6 +168,7 @@ struct q6v5 {
struct qcom_rproc_subdev smd_subdev;
struct qcom_rproc_ssr ssr_subdev;
struct qcom_sysmon *sysmon;
bool need_mem_protection;
int mpss_perm;
int mba_perm;
......@@ -939,9 +940,6 @@ static irqreturn_t q6v5_wdog_interrupt(int irq, void *dev)
rproc_report_crash(qproc->rproc, RPROC_WATCHDOG);
if (!IS_ERR(msg))
msg[0] = '\0';
return IRQ_HANDLED;
}
......@@ -959,9 +957,6 @@ static irqreturn_t q6v5_fatal_interrupt(int irq, void *dev)
rproc_report_crash(qproc->rproc, RPROC_FATAL_ERROR);
if (!IS_ERR(msg))
msg[0] = '\0';
return IRQ_HANDLED;
}
......@@ -1215,6 +1210,7 @@ static int q6v5_probe(struct platform_device *pdev)
qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS);
qcom_add_smd_subdev(rproc, &qproc->smd_subdev);
qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss");
qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12);
ret = rproc_add(rproc);
if (ret)
......@@ -1234,6 +1230,7 @@ static int q6v5_remove(struct platform_device *pdev)
rproc_del(qproc->rproc);
qcom_remove_sysmon_subdev(qproc->sysmon);
qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev);
qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev);
rproc_free(qproc->rproc);
......
This diff is collapsed.
......@@ -40,6 +40,7 @@
#define WCNSS_CRASH_REASON_SMEM 422
#define WCNSS_FIRMWARE_NAME "wcnss.mdt"
#define WCNSS_PAS_ID 6
#define WCNSS_SSCTL_ID 0x13
#define WCNSS_SPARE_NVBIN_DLND BIT(25)
......@@ -98,6 +99,7 @@ struct qcom_wcnss {
size_t mem_size;
struct qcom_rproc_subdev smd_subdev;
struct qcom_sysmon *sysmon;
};
static const struct wcnss_data riva_data = {
......@@ -153,7 +155,8 @@ static int wcnss_load(struct rproc *rproc, const struct firmware *fw)
struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
return qcom_mdt_load(wcnss->dev, fw, rproc->firmware, WCNSS_PAS_ID,
wcnss->mem_region, wcnss->mem_phys, wcnss->mem_size);
wcnss->mem_region, wcnss->mem_phys,
wcnss->mem_size, &wcnss->mem_reloc);
}
static void wcnss_indicate_nv_download(struct qcom_wcnss *wcnss)
......@@ -308,6 +311,7 @@ static const struct rproc_ops wcnss_ops = {
.start = wcnss_start,
.stop = wcnss_stop,
.da_to_va = wcnss_da_to_va,
.parse_fw = qcom_register_dump_segments,
.load = wcnss_load,
};
......@@ -332,9 +336,6 @@ static irqreturn_t wcnss_fatal_interrupt(int irq, void *dev)
rproc_report_crash(wcnss->rproc, RPROC_FATAL_ERROR);
if (!IS_ERR(msg))
msg[0] = '\0';
return IRQ_HANDLED;
}
......@@ -551,6 +552,7 @@ static int wcnss_probe(struct platform_device *pdev)
}
qcom_add_smd_subdev(rproc, &wcnss->smd_subdev);
wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID);
ret = rproc_add(rproc);
if (ret)
......@@ -573,6 +575,7 @@ static int wcnss_remove(struct platform_device *pdev)
qcom_smem_state_put(wcnss->state);
rproc_del(wcnss->rproc);
qcom_remove_sysmon_subdev(wcnss->sysmon);
qcom_remove_smd_subdev(wcnss->rproc, &wcnss->smd_subdev);
rproc_free(wcnss->rproc);
......
......@@ -33,6 +33,7 @@
#include <linux/firmware.h>
#include <linux/string.h>
#include <linux/debugfs.h>
#include <linux/devcoredump.h>
#include <linux/remoteproc.h>
#include <linux/iommu.h>
#include <linux/idr.h>
......@@ -307,7 +308,7 @@ static int rproc_vdev_do_probe(struct rproc_subdev *subdev)
return rproc_add_virtio_dev(rvdev, rvdev->id);
}
static void rproc_vdev_do_remove(struct rproc_subdev *subdev)
static void rproc_vdev_do_remove(struct rproc_subdev *subdev, bool crashed)
{
struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev);
......@@ -788,17 +789,31 @@ static int rproc_probe_subdevices(struct rproc *rproc)
unroll_registration:
list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node)
subdev->remove(subdev);
subdev->remove(subdev, true);
return ret;
}
static void rproc_remove_subdevices(struct rproc *rproc)
static void rproc_remove_subdevices(struct rproc *rproc, bool crashed)
{
struct rproc_subdev *subdev;
list_for_each_entry_reverse(subdev, &rproc->subdevs, node)
subdev->remove(subdev);
subdev->remove(subdev, crashed);
}
/**
* rproc_coredump_cleanup() - clean up dump_segments list
* @rproc: the remote processor handle
*/
static void rproc_coredump_cleanup(struct rproc *rproc)
{
struct rproc_dump_segment *entry, *tmp;
list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) {
list_del(&entry->node);
kfree(entry);
}
}
/**
......@@ -848,6 +863,8 @@ static void rproc_resource_cleanup(struct rproc *rproc)
/* clean up remote vdev entries */
list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node)
kref_put(&rvdev->refcount, rproc_vdev_release);
rproc_coredump_cleanup(rproc);
}
static int rproc_start(struct rproc *rproc, const struct firmware *fw)
......@@ -927,8 +944,8 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
rproc->bootaddr = rproc_get_boot_addr(rproc, fw);
/* load resource table */
ret = rproc_load_rsc_table(rproc, fw);
/* Load resource table, core dump segment list etc from the firmware */
ret = rproc_parse_fw(rproc, fw);
if (ret)
goto disable_iommu;
......@@ -992,13 +1009,13 @@ static int rproc_trigger_auto_boot(struct rproc *rproc)
return ret;
}
static int rproc_stop(struct rproc *rproc)
static int rproc_stop(struct rproc *rproc, bool crashed)
{
struct device *dev = &rproc->dev;
int ret;
/* remove any subdevices for the remote processor */
rproc_remove_subdevices(rproc);
rproc_remove_subdevices(rproc, crashed);
/* the installed resource table is no longer accessible */
rproc->table_ptr = rproc->cached_table;
......@@ -1017,6 +1034,113 @@ static int rproc_stop(struct rproc *rproc)
return 0;
}
/**
* rproc_coredump_add_segment() - add segment of device memory to coredump
* @rproc: handle of a remote processor
* @da: device address
* @size: size of segment
*
* Add device memory to the list of segments to be included in a coredump for
* the remoteproc.
*
* Return: 0 on success, negative errno on error.
*/
int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size)
{
struct rproc_dump_segment *segment;
segment = kzalloc(sizeof(*segment), GFP_KERNEL);
if (!segment)
return -ENOMEM;
segment->da = da;
segment->size = size;
list_add_tail(&segment->node, &rproc->dump_segments);
return 0;
}
EXPORT_SYMBOL(rproc_coredump_add_segment);
/**
* rproc_coredump() - perform coredump
* @rproc: rproc handle
*
* This function will generate an ELF header for the registered segments
* and create a devcoredump device associated with rproc.
*/
static void rproc_coredump(struct rproc *rproc)
{
struct rproc_dump_segment *segment;
struct elf32_phdr *phdr;
struct elf32_hdr *ehdr;
size_t data_size;
size_t offset;
void *data;
void *ptr;
int phnum = 0;
if (list_empty(&rproc->dump_segments))
return;
data_size = sizeof(*ehdr);
list_for_each_entry(segment, &rproc->dump_segments, node) {
data_size += sizeof(*phdr) + segment->size;
phnum++;
}
data = vmalloc(data_size);
if (!data)
return;
ehdr = data;
memset(ehdr, 0, sizeof(*ehdr));
memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
ehdr->e_ident[EI_CLASS] = ELFCLASS32;
ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
ehdr->e_ident[EI_VERSION] = EV_CURRENT;
ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE;
ehdr->e_type = ET_CORE;
ehdr->e_machine = EM_NONE;
ehdr->e_version = EV_CURRENT;
ehdr->e_entry = rproc->bootaddr;
ehdr->e_phoff = sizeof(*ehdr);
ehdr->e_ehsize = sizeof(*ehdr);
ehdr->e_phentsize = sizeof(*phdr);
ehdr->e_phnum = phnum;
phdr = data + ehdr->e_phoff;
offset = ehdr->e_phoff + sizeof(*phdr) * ehdr->e_phnum;
list_for_each_entry(segment, &rproc->dump_segments, node) {
memset(phdr, 0, sizeof(*phdr));
phdr->p_type = PT_LOAD;
phdr->p_offset = offset;
phdr->p_vaddr = segment->da;
phdr->p_paddr = segment->da;
phdr->p_filesz = segment->size;
phdr->p_memsz = segment->size;
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,
"invalid coredump segment (%pad, %zu)\n",
&segment->da, segment->size);
memset(data + offset, 0xff, segment->size);
} else {
memcpy(data + offset, ptr, segment->size);
}
offset += phdr->p_filesz;
phdr++;
}
dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL);
}
/**
* rproc_trigger_recovery() - recover a remoteproc
* @rproc: the remote processor
......@@ -1039,10 +1163,13 @@ int rproc_trigger_recovery(struct rproc *rproc)
if (ret)
return ret;
ret = rproc_stop(rproc);
ret = rproc_stop(rproc, false);
if (ret)
goto unlock_mutex;
/* generate coredump */
rproc_coredump(rproc);
/* load firmware */
ret = request_firmware(&firmware_p, rproc->firmware, dev);
if (ret < 0) {
......@@ -1189,7 +1316,7 @@ void rproc_shutdown(struct rproc *rproc)
if (!atomic_dec_and_test(&rproc->power))
goto out;
ret = rproc_stop(rproc);
ret = rproc_stop(rproc, true);
if (ret) {
atomic_inc(&rproc->power);
goto out;
......@@ -1428,7 +1555,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
/* Default to ELF loader if no load function is specified */
if (!rproc->ops->load) {
rproc->ops->load = rproc_elf_load_segments;
rproc->ops->load_rsc_table = rproc_elf_load_rsc_table;
rproc->ops->parse_fw = rproc_elf_load_rsc_table;
rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table;
rproc->ops->sanity_check = rproc_elf_sanity_check;
rproc->ops->get_boot_addr = rproc_elf_get_boot_addr;
......@@ -1443,6 +1570,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
INIT_LIST_HEAD(&rproc->traces);
INIT_LIST_HEAD(&rproc->rvdevs);
INIT_LIST_HEAD(&rproc->subdevs);
INIT_LIST_HEAD(&rproc->dump_segments);
INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work);
......@@ -1535,7 +1663,7 @@ EXPORT_SYMBOL(rproc_del);
void rproc_add_subdev(struct rproc *rproc,
struct rproc_subdev *subdev,
int (*probe)(struct rproc_subdev *subdev),
void (*remove)(struct rproc_subdev *subdev))
void (*remove)(struct rproc_subdev *subdev, bool crashed))
{
subdev->probe = probe;
subdev->remove = remove;
......
......@@ -88,11 +88,10 @@ int rproc_load_segments(struct rproc *rproc, const struct firmware *fw)
return -EINVAL;
}
static inline int rproc_load_rsc_table(struct rproc *rproc,
const struct firmware *fw)
static inline int rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
{
if (rproc->ops->load_rsc_table)
return rproc->ops->load_rsc_table(rproc, fw);
if (rproc->ops->parse_fw)
return rproc->ops->parse_fw(rproc, fw);
return 0;
}