From patchwork Tue Dec 26 20:38:29 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bjorn Andersson X-Patchwork-Id: 10133267 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 72CE66023A for ; Tue, 26 Dec 2017 20:39:45 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 62F172D275 for ; Tue, 26 Dec 2017 20:39:45 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 57B992D295; Tue, 26 Dec 2017 20:39:45 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C09DE2D275 for ; Tue, 26 Dec 2017 20:39:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751220AbdLZUjf (ORCPT ); Tue, 26 Dec 2017 15:39:35 -0500 Received: from mail-pf0-f195.google.com ([209.85.192.195]:36857 "EHLO mail-pf0-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751175AbdLZUih (ORCPT ); Tue, 26 Dec 2017 15:38:37 -0500 Received: by mail-pf0-f195.google.com with SMTP id p84so19059355pfd.3 for ; Tue, 26 Dec 2017 12:38:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=GID84wUKoSZY9877w/R3D7KzfNyr3j31muYokwcu6b0=; b=dt8SiZs9fbP3SbrglHcqwTYwDy9hylK0agmEVCMW439E6YuY0LBl2kFUkeowCcJm1o iSrIBYeM4WuUEYaqfyz7gzQ0gVT2MrCAGExPvfFHkEESJWHRcg8Ptp9OcCQh5c3W9j/X nYzh/fTu78FFIGHes3GoKHUQvdHW+rprGrliQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=GID84wUKoSZY9877w/R3D7KzfNyr3j31muYokwcu6b0=; b=Nhkm7LcUM7lHh22LknVh4n1NALwVZ8He5u2OVcsfwemmWuZfH2kz7rEq2wpgtToGRb IqT8kWpc6RYWsqCz5sUrpqLkkZmdvBc1lksmyzfmIwDWlI6717MmQq5z9chAFw2o1Cpr FcYINzRPtn3WqQX+LxpmAR8umzrhvxSxLVEkWXhl0fkbjVnecBy2Tkf2yBlNUw3+wK87 ZivLp6Q2+ORMFO+7QVmCq89whamZj+A9bG/LzGLJw2/RFjfIT3Rq2LrjaecWylkfU2fY uFd6VEUOv/jKCtlq6KpI7DBvhEfFrx8IxSi+RCpAZG9K+b7E4oPkCmvovAVgNcdxKvau o+Ag== X-Gm-Message-State: AKGB3mIxJ8JxUtlJaBxmWTcE+RDTbsaCGhpOU+D0y519T2V8JRGLnOTX mUR86d+MmGp3Ipw9pXNR4RGRFQ== X-Google-Smtp-Source: ACJfBoud5jV/c5N4omZBSA+K1/nXv60QcWNdTE6idAFXxjP0eCmGNQ9gKDcms4pPZTznOmKJv4jvvA== X-Received: by 10.99.105.134 with SMTP id e128mr22944896pgc.152.1514320717075; Tue, 26 Dec 2017 12:38:37 -0800 (PST) Received: from localhost.localdomain (104-188-17-28.lightspeed.sndgca.sbcglobal.net. [104.188.17.28]) by smtp.gmail.com with ESMTPSA id 68sm58746339pfx.186.2017.12.26.12.38.35 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 26 Dec 2017 12:38:36 -0800 (PST) From: Bjorn Andersson To: Ohad Ben-Cohen , Bjorn Andersson Cc: linux-remoteproc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-soc@vger.kernel.org, Suman Anna , Avaneesh Kumar Dwivedi Subject: [PATCH v2 1/4] remoteproc: Add remote processor coredump support Date: Tue, 26 Dec 2017 12:38:29 -0800 Message-Id: <20171226203832.14928-2-bjorn.andersson@linaro.org> X-Mailer: git-send-email 2.15.0 In-Reply-To: <20171226203832.14928-1-bjorn.andersson@linaro.org> References: <20171226203832.14928-1-bjorn.andersson@linaro.org> Sender: linux-remoteproc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-remoteproc@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Sarangdhar Joshi 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 [bjorn: Use vmalloc instead of composing the ELF on the fly] Signed-off-by: Bjorn Andersson --- Changes since v1: - vmalloc() memory to build ELF file before registering devcoredump - life cycle of segment list is from boot to shutdown (not start/shutdown) - add WANT_DEV_COREDUMP select - use dma_addr_t to denote device addresses drivers/remoteproc/Kconfig | 1 + drivers/remoteproc/remoteproc_core.c | 128 +++++++++++++++++++++++++++++++++++ include/linux/remoteproc.h | 18 +++++ 3 files changed, 147 insertions(+) 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 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. 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 @@ #include #include #include +#include #include #include #include @@ -801,6 +802,20 @@ static void rproc_remove_subdevices(struct rproc *rproc) subdev->remove(subdev); } +/** + * 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); + } +} + /** * rproc_resource_cleanup() - clean up and free all acquired resources * @rproc: rproc handle @@ -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) @@ -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 @@ -1043,6 +1167,9 @@ int rproc_trigger_recovery(struct rproc *rproc) if (ret) goto unlock_mutex; + /* generate coredump */ + rproc_coredump(rproc); + /* load firmware */ ret = request_firmware(&firmware_p, rproc->firmware, dev); if (ret < 0) { @@ -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); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 32a3bf351919..de6e20a3f061 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -393,6 +393,21 @@ enum rproc_crash_type { RPROC_FATAL_ERROR, }; +/** + * struct rproc_dump_segment - segment info from ELF header + * @node: list node related to the rproc segment list + * @da: device address of the segment + * @size: size of the segment + */ +struct rproc_dump_segment { + struct list_head node; + + dma_addr_t da; + size_t size; + + loff_t offset; +}; + /** * struct rproc - represents a physical remote processor device * @node: list node of this rproc object @@ -423,6 +438,7 @@ enum rproc_crash_type { * @cached_table: copy of the resource table * @table_sz: size of @cached_table * @has_iommu: flag to indicate if remote processor is behind an MMU + * @dump_segments: list of segments in the firmware */ struct rproc { struct list_head node; @@ -454,6 +470,7 @@ struct rproc { size_t table_sz; bool has_iommu; bool auto_boot; + struct list_head dump_segments; }; /** @@ -533,6 +550,7 @@ void rproc_free(struct rproc *rproc); 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); static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev) {