aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSarangdhar Joshi <spjoshi@codeaurora.org>2018-01-05 19:04:17 -0500
committerBjorn Andersson <bjorn.andersson@linaro.org>2018-02-12 14:05:29 -0500
commit2666ca9197e3d352f43b02d7dfb7c6dd72e7c614 (patch)
treeb9c60aed35f13ff5c2fb519bf31eec2ed5d9c644
parent2d02d158047643060b7d6e0829fcc81518ce663d (diff)
remoteproc: Add remote processor coredump support
As the remoteproc framework restarts the remote processor after a fatal event, it's useful to be able to acquire a coredump of the remote processor's state, for post mortem debugging. This patch introduces a mechanism for extracting the memory contents after the remote has stopped and before the restart sequence has begun in the recovery path. The remoteproc framework builds the core dump in memory and use devcoredump to expose this to user space. Signed-off-by: Sarangdhar Joshi <spjoshi@codeaurora.org> [bjorn: Use vmalloc instead of composing the ELF on the fly] Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
-rw-r--r--drivers/remoteproc/Kconfig1
-rw-r--r--drivers/remoteproc/remoteproc_core.c128
-rw-r--r--include/linux/remoteproc.h18
3 files changed, 147 insertions, 0 deletions
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index b609e1d3654b..3e4bca77188d 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -6,6 +6,7 @@ config REMOTEPROC
6 select CRC32 6 select CRC32
7 select FW_LOADER 7 select FW_LOADER
8 select VIRTIO 8 select VIRTIO
9 select WANT_DEV_COREDUMP
9 help 10 help
10 Support for remote processors (such as DSP coprocessors). These 11 Support for remote processors (such as DSP coprocessors). These
11 are mainly used on embedded systems. 12 are mainly used on embedded systems.
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 4170dfbd93bd..5af7547b9d8d 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -33,6 +33,7 @@
33#include <linux/firmware.h> 33#include <linux/firmware.h>
34#include <linux/string.h> 34#include <linux/string.h>
35#include <linux/debugfs.h> 35#include <linux/debugfs.h>
36#include <linux/devcoredump.h>
36#include <linux/remoteproc.h> 37#include <linux/remoteproc.h>
37#include <linux/iommu.h> 38#include <linux/iommu.h>
38#include <linux/idr.h> 39#include <linux/idr.h>
@@ -802,6 +803,20 @@ static void rproc_remove_subdevices(struct rproc *rproc)
802} 803}
803 804
804/** 805/**
806 * rproc_coredump_cleanup() - clean up dump_segments list
807 * @rproc: the remote processor handle
808 */
809static void rproc_coredump_cleanup(struct rproc *rproc)
810{
811 struct rproc_dump_segment *entry, *tmp;
812
813 list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) {
814 list_del(&entry->node);
815 kfree(entry);
816 }
817}
818
819/**
805 * rproc_resource_cleanup() - clean up and free all acquired resources 820 * rproc_resource_cleanup() - clean up and free all acquired resources
806 * @rproc: rproc handle 821 * @rproc: rproc handle
807 * 822 *
@@ -848,6 +863,8 @@ static void rproc_resource_cleanup(struct rproc *rproc)
848 /* clean up remote vdev entries */ 863 /* clean up remote vdev entries */
849 list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node) 864 list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node)
850 kref_put(&rvdev->refcount, rproc_vdev_release); 865 kref_put(&rvdev->refcount, rproc_vdev_release);
866
867 rproc_coredump_cleanup(rproc);
851} 868}
852 869
853static int rproc_start(struct rproc *rproc, const struct firmware *fw) 870static int rproc_start(struct rproc *rproc, const struct firmware *fw)
@@ -1018,6 +1035,113 @@ static int rproc_stop(struct rproc *rproc)
1018} 1035}
1019 1036
1020/** 1037/**
1038 * rproc_coredump_add_segment() - add segment of device memory to coredump
1039 * @rproc: handle of a remote processor
1040 * @da: device address
1041 * @size: size of segment
1042 *
1043 * Add device memory to the list of segments to be included in a coredump for
1044 * the remoteproc.
1045 *
1046 * Return: 0 on success, negative errno on error.
1047 */
1048int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size)
1049{
1050 struct rproc_dump_segment *segment;
1051
1052 segment = kzalloc(sizeof(*segment), GFP_KERNEL);
1053 if (!segment)
1054 return -ENOMEM;
1055
1056 segment->da = da;
1057 segment->size = size;
1058
1059 list_add_tail(&segment->node, &rproc->dump_segments);
1060
1061 return 0;
1062}
1063EXPORT_SYMBOL(rproc_coredump_add_segment);
1064
1065/**
1066 * rproc_coredump() - perform coredump
1067 * @rproc: rproc handle
1068 *
1069 * This function will generate an ELF header for the registered segments
1070 * and create a devcoredump device associated with rproc.
1071 */
1072static void rproc_coredump(struct rproc *rproc)
1073{
1074 struct rproc_dump_segment *segment;
1075 struct elf32_phdr *phdr;
1076 struct elf32_hdr *ehdr;
1077 size_t data_size;
1078 size_t offset;
1079 void *data;
1080 void *ptr;
1081 int phnum = 0;
1082
1083 if (list_empty(&rproc->dump_segments))
1084 return;
1085
1086 data_size = sizeof(*ehdr);
1087 list_for_each_entry(segment, &rproc->dump_segments, node) {
1088 data_size += sizeof(*phdr) + segment->size;
1089
1090 phnum++;
1091 }
1092
1093 data = vmalloc(data_size);
1094 if (!data)
1095 return;
1096
1097 ehdr = data;
1098
1099 memset(ehdr, 0, sizeof(*ehdr));
1100 memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
1101 ehdr->e_ident[EI_CLASS] = ELFCLASS32;
1102 ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
1103 ehdr->e_ident[EI_VERSION] = EV_CURRENT;
1104 ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE;
1105 ehdr->e_type = ET_CORE;
1106 ehdr->e_machine = EM_NONE;
1107 ehdr->e_version = EV_CURRENT;
1108 ehdr->e_entry = rproc->bootaddr;
1109 ehdr->e_phoff = sizeof(*ehdr);
1110 ehdr->e_ehsize = sizeof(*ehdr);
1111 ehdr->e_phentsize = sizeof(*phdr);
1112 ehdr->e_phnum = phnum;
1113
1114 phdr = data + ehdr->e_phoff;
1115 offset = ehdr->e_phoff + sizeof(*phdr) * ehdr->e_phnum;
1116 list_for_each_entry(segment, &rproc->dump_segments, node) {
1117 memset(phdr, 0, sizeof(*phdr));
1118 phdr->p_type = PT_LOAD;
1119 phdr->p_offset = offset;
1120 phdr->p_vaddr = segment->da;
1121 phdr->p_paddr = segment->da;
1122 phdr->p_filesz = segment->size;
1123 phdr->p_memsz = segment->size;
1124 phdr->p_flags = PF_R | PF_W | PF_X;
1125 phdr->p_align = 0;
1126
1127 ptr = rproc_da_to_va(rproc, segment->da, segment->size);
1128 if (!ptr) {
1129 dev_err(&rproc->dev,
1130 "invalid coredump segment (%pad, %zu)\n",
1131 &segment->da, segment->size);
1132 memset(data + offset, 0xff, segment->size);
1133 } else {
1134 memcpy(data + offset, ptr, segment->size);
1135 }
1136
1137 offset += phdr->p_filesz;
1138 phdr++;
1139 }
1140
1141 dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL);
1142}
1143
1144/**
1021 * rproc_trigger_recovery() - recover a remoteproc 1145 * rproc_trigger_recovery() - recover a remoteproc
1022 * @rproc: the remote processor 1146 * @rproc: the remote processor
1023 * 1147 *
@@ -1043,6 +1167,9 @@ int rproc_trigger_recovery(struct rproc *rproc)
1043 if (ret) 1167 if (ret)
1044 goto unlock_mutex; 1168 goto unlock_mutex;
1045 1169
1170 /* generate coredump */
1171 rproc_coredump(rproc);
1172
1046 /* load firmware */ 1173 /* load firmware */
1047 ret = request_firmware(&firmware_p, rproc->firmware, dev); 1174 ret = request_firmware(&firmware_p, rproc->firmware, dev);
1048 if (ret < 0) { 1175 if (ret < 0) {
@@ -1443,6 +1570,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
1443 INIT_LIST_HEAD(&rproc->traces); 1570 INIT_LIST_HEAD(&rproc->traces);
1444 INIT_LIST_HEAD(&rproc->rvdevs); 1571 INIT_LIST_HEAD(&rproc->rvdevs);
1445 INIT_LIST_HEAD(&rproc->subdevs); 1572 INIT_LIST_HEAD(&rproc->subdevs);
1573 INIT_LIST_HEAD(&rproc->dump_segments);
1446 1574
1447 INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work); 1575 INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work);
1448 1576
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index 728d421fffe9..b60c3a31b75d 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -395,6 +395,21 @@ enum rproc_crash_type {
395}; 395};
396 396
397/** 397/**
398 * struct rproc_dump_segment - segment info from ELF header
399 * @node: list node related to the rproc segment list
400 * @da: device address of the segment
401 * @size: size of the segment
402 */
403struct rproc_dump_segment {
404 struct list_head node;
405
406 dma_addr_t da;
407 size_t size;
408
409 loff_t offset;
410};
411
412/**
398 * struct rproc - represents a physical remote processor device 413 * struct rproc - represents a physical remote processor device
399 * @node: list node of this rproc object 414 * @node: list node of this rproc object
400 * @domain: iommu domain 415 * @domain: iommu domain
@@ -424,6 +439,7 @@ enum rproc_crash_type {
424 * @cached_table: copy of the resource table 439 * @cached_table: copy of the resource table
425 * @table_sz: size of @cached_table 440 * @table_sz: size of @cached_table
426 * @has_iommu: flag to indicate if remote processor is behind an MMU 441 * @has_iommu: flag to indicate if remote processor is behind an MMU
442 * @dump_segments: list of segments in the firmware
427 */ 443 */
428struct rproc { 444struct rproc {
429 struct list_head node; 445 struct list_head node;
@@ -455,6 +471,7 @@ struct rproc {
455 size_t table_sz; 471 size_t table_sz;
456 bool has_iommu; 472 bool has_iommu;
457 bool auto_boot; 473 bool auto_boot;
474 struct list_head dump_segments;
458}; 475};
459 476
460/** 477/**
@@ -534,6 +551,7 @@ void rproc_free(struct rproc *rproc);
534int rproc_boot(struct rproc *rproc); 551int rproc_boot(struct rproc *rproc);
535void rproc_shutdown(struct rproc *rproc); 552void rproc_shutdown(struct rproc *rproc);
536void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type); 553void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type);
554int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size);
537 555
538static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev) 556static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev)
539{ 557{