Merge "remoteproc: qcom: pas: Rename subsystem minidump elf name"

This commit is contained in:
qctecmdr 2022-06-10 12:03:21 -07:00 committed by Gerrit - the friendly Code Review server
commit 1522b16b3e
5 changed files with 259 additions and 5 deletions

View File

@ -5,6 +5,7 @@
* Copyright (C) 2016 Linaro Ltd
* Copyright (C) 2015 Sony Mobile Communications Inc
* Copyright (c) 2012-2013, 2020-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/firmware.h>
@ -17,9 +18,11 @@
#include <linux/slab.h>
#include <linux/soc/qcom/mdt_loader.h>
#include <linux/soc/qcom/smem.h>
#include <linux/devcoredump.h>
#include <trace/hooks/remoteproc.h>
#include <trace/events/rproc_qcom.h>
#include "remoteproc_elf_helpers.h"
#include "remoteproc_internal.h"
#include "qcom_common.h"
@ -171,7 +174,114 @@ static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsy
return 0;
}
void qcom_minidump(struct rproc *rproc, unsigned int minidump_id, rproc_dumpfn_t dumpfn)
static void qcom_rproc_minidump(struct rproc *rproc, struct device *md_dev)
{
struct rproc_dump_segment *segment;
void *shdr;
void *ehdr;
size_t data_size;
size_t strtbl_size = 0;
size_t strtbl_index = 1;
size_t offset;
void *data;
u8 class = rproc->elf_class;
int shnum;
unsigned int dump_conf = rproc->dump_conf;
char *str_tbl = "STR_TBL";
if (list_empty(&rproc->dump_segments) ||
dump_conf == RPROC_COREDUMP_DISABLED)
return;
if (class == ELFCLASSNONE) {
dev_err(&rproc->dev, "Elf class is not set\n");
return;
}
/*
* We allocate two extra section headers. The first one is null.
* Second section header is for the string table. Also space is
* allocated for string table.
*/
data_size = elf_size_of_hdr(class) + 2 * elf_size_of_shdr(class);
shnum = 2;
/* the extra byte is for the null character at index 0 */
strtbl_size += strlen(str_tbl) + 2;
list_for_each_entry(segment, &rproc->dump_segments, node) {
data_size += elf_size_of_shdr(class);
strtbl_size += strlen(segment->priv) + 1;
data_size += segment->size;
shnum++;
}
data_size += strtbl_size;
data = vmalloc(data_size);
if (!data)
return;
ehdr = data;
memset(ehdr, 0, elf_size_of_hdr(class));
/* e_ident field is common for both elf32 and elf64 */
elf_hdr_init_ident(ehdr, class);
elf_hdr_set_e_type(class, ehdr, ET_CORE);
elf_hdr_set_e_machine(class, ehdr, rproc->elf_machine);
elf_hdr_set_e_version(class, ehdr, EV_CURRENT);
elf_hdr_set_e_entry(class, ehdr, rproc->bootaddr);
elf_hdr_set_e_shoff(class, ehdr, elf_size_of_hdr(class));
elf_hdr_set_e_ehsize(class, ehdr, elf_size_of_hdr(class));
elf_hdr_set_e_shentsize(class, ehdr, elf_size_of_shdr(class));
elf_hdr_set_e_shnum(class, ehdr, shnum);
elf_hdr_set_e_shstrndx(class, ehdr, 1);
/*
* The zeroth index of the section header is reserved and is rarely used.
* Set the section header as null (SHN_UNDEF) and move to the next one.
*/
shdr = data + elf_hdr_get_e_shoff(class, ehdr);
memset(shdr, 0, elf_size_of_shdr(class));
shdr += elf_size_of_shdr(class);
/* Initialize the string table. */
offset = elf_hdr_get_e_shoff(class, ehdr) +
elf_size_of_shdr(class) * elf_hdr_get_e_shnum(class, ehdr);
memset(data + offset, 0, strtbl_size);
/* Fill in the string table section header. */
memset(shdr, 0, elf_size_of_shdr(class));
elf_shdr_set_sh_type(class, shdr, SHT_STRTAB);
elf_shdr_set_sh_offset(class, shdr, offset);
elf_shdr_set_sh_size(class, shdr, strtbl_size);
elf_shdr_set_sh_entsize(class, shdr, 0);
elf_shdr_set_sh_flags(class, shdr, 0);
elf_shdr_set_sh_name(class, shdr, elf_strtbl_add(str_tbl, ehdr, class, &strtbl_index));
offset += elf_shdr_get_sh_size(class, shdr);
shdr += elf_size_of_shdr(class);
list_for_each_entry(segment, &rproc->dump_segments, node) {
memset(shdr, 0, elf_size_of_shdr(class));
elf_shdr_set_sh_type(class, shdr, SHT_PROGBITS);
elf_shdr_set_sh_offset(class, shdr, offset);
elf_shdr_set_sh_addr(class, shdr, segment->da);
elf_shdr_set_sh_size(class, shdr, segment->size);
elf_shdr_set_sh_entsize(class, shdr, 0);
elf_shdr_set_sh_flags(class, shdr, SHF_WRITE);
elf_shdr_set_sh_name(class, shdr,
elf_strtbl_add(segment->priv, ehdr, class, &strtbl_index));
/* No need to copy segments for inline dumps */
segment->dump(rproc, segment, data + offset, 0, segment->size);
offset += elf_shdr_get_sh_size(class, shdr);
shdr += elf_size_of_shdr(class);
}
dev_coredumpv(md_dev, data, data_size, GFP_KERNEL);
}
void qcom_minidump(struct rproc *rproc, struct device *md_dev,
unsigned int minidump_id, rproc_dumpfn_t dumpfn)
{
int ret;
struct minidump_subsystem *subsystem;
@ -213,9 +323,10 @@ void qcom_minidump(struct rproc *rproc, unsigned int minidump_id, rproc_dumpfn_t
}
if (rproc->elf_class == ELFCLASS64)
rproc_coredump_using_sections(rproc);
qcom_rproc_minidump(rproc, md_dev);
else
rproc_coredump(rproc);
clean_minidump:
qcom_minidump_cleanup(rproc);
}

View File

@ -58,7 +58,8 @@ extern bool qcom_device_shutdown_in_progress;
typedef void (*rproc_dumpfn_t)(struct rproc *rproc, struct rproc_dump_segment *segment,
void *dest, size_t offset, size_t size);
void qcom_minidump(struct rproc *rproc, unsigned int minidump_id, rproc_dumpfn_t dumpfn);
void qcom_minidump(struct rproc *rproc, struct device *md_dev,
unsigned int minidump_id, rproc_dumpfn_t dumpfn);
void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink,
const char *ssr_name);

View File

@ -5,6 +5,7 @@
* Copyright (C) 2016 Linaro Ltd
* Copyright (C) 2014 Sony Mobile Communications AB
* Copyright (c) 2012-2013, 2020-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/clk.h>
@ -28,6 +29,7 @@
#include <linux/soc/qcom/smem_state.h>
#include <linux/soc/qcom/qcom_aoss.h>
#include <trace/events/rproc_qcom.h>
#include <soc/qcom/qcom_ramdump.h>
#include "qcom_common.h"
#include "qcom_pil_info.h"
@ -66,6 +68,7 @@ struct adsp_data {
struct qcom_adsp {
struct device *dev;
struct device *minidump_dev;
struct rproc *rproc;
struct qcom_q6v5 q6v5;
@ -144,7 +147,7 @@ static void adsp_minidump(struct rproc *rproc)
if (rproc->dump_conf == RPROC_COREDUMP_DISABLED)
goto exit;
qcom_minidump(rproc, adsp->minidump_id, adsp_segment_dump);
qcom_minidump(rproc, adsp->minidump_dev, adsp->minidump_id, adsp_segment_dump);
exit:
trace_rproc_qcom_event(dev_name(adsp->dev), "adsp_minidump", "exit");
@ -719,6 +722,7 @@ static int adsp_probe(struct platform_device *pdev)
struct rproc *rproc;
const char *fw_name;
const struct rproc_ops *ops = &adsp_ops;
char md_dev_name[32];
int ret;
desc = of_device_get_match_data(&pdev->dev);
@ -828,11 +832,21 @@ static int adsp_probe(struct platform_device *pdev)
if (ret)
goto remove_subdevs;
snprintf(md_dev_name, ARRAY_SIZE(md_dev_name), "%s-md", pdev->dev.of_node->name);
adsp->minidump_dev = qcom_create_ramdump_device(md_dev_name, NULL);
if (!adsp->minidump_dev) {
dev_err(&pdev->dev, "Unable to create %s minidump device.\n", md_dev_name);
ret = -ENOMEM;
goto remove_attr_txn_id;
}
ret = rproc_add(rproc);
if (ret)
goto remove_attr_txn_id;
goto destroy_minidump_dev;
return 0;
destroy_minidump_dev:
qcom_destroy_ramdump_device(adsp->minidump_dev);
remove_attr_txn_id:
device_remove_file(adsp->dev, &dev_attr_txn_id);
remove_subdevs:
@ -854,6 +868,7 @@ static int adsp_remove(struct platform_device *pdev)
struct qcom_adsp *adsp = platform_get_drvdata(pdev);
rproc_del(adsp->rproc);
qcom_destroy_ramdump_device(adsp->minidump_dev);
device_remove_file(adsp->dev, &dev_attr_txn_id);
qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev);
qcom_remove_sysmon_subdev(adsp->sysmon);

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/kernel.h>
@ -41,6 +42,21 @@ do { \
#define set_phdr_property(arg, class, member, value) \
set_xhdr_property(phdr, arg, class, member, value)
#define RAMDUMP_NUM_DEVICES 256
#define RAMDUMP_NAME "ramdump"
static struct class *ramdump_class;
static dev_t ramdump_dev;
static DEFINE_MUTEX(rd_minor_mutex);
static DEFINE_IDA(rd_minor_id);
static bool ramdump_devnode_inited;
struct ramdump_device {
char name[256];
struct cdev cdev;
struct device *dev;
};
struct qcom_ramdump_desc {
void *data;
struct completion dump_done;
@ -250,6 +266,106 @@ int qcom_fw_elf_dump(struct firmware *fw, struct device *dev)
}
EXPORT_SYMBOL(qcom_fw_elf_dump);
static int ramdump_devnode_init(void)
{
int ret;
ramdump_class = class_create(THIS_MODULE, RAMDUMP_NAME);
ret = alloc_chrdev_region(&ramdump_dev, 0, RAMDUMP_NUM_DEVICES,
RAMDUMP_NAME);
if (ret) {
pr_err("%s: unable to allocate major\n", __func__);
return ret;
}
ramdump_devnode_inited = true;
return 0;
}
void *qcom_create_ramdump_device(const char *dev_name, struct device *parent)
{
int ret, minor;
struct ramdump_device *rd_dev;
if (!dev_name) {
pr_err("%s: Invalid device name.\n", __func__);
return NULL;
}
mutex_lock(&rd_minor_mutex);
if (!ramdump_devnode_inited) {
ret = ramdump_devnode_init();
if (ret) {
mutex_unlock(&rd_minor_mutex);
return ERR_PTR(ret);
}
}
mutex_unlock(&rd_minor_mutex);
rd_dev = kzalloc(sizeof(struct ramdump_device), GFP_KERNEL);
if (!rd_dev)
return NULL;
/* get a minor number */
minor = ida_simple_get(&rd_minor_id, 0, RAMDUMP_NUM_DEVICES,
GFP_KERNEL);
if (minor < 0) {
pr_err("%s: No more minor numbers left! rc:%d\n", __func__,
minor);
ret = -ENODEV;
goto fail_out_of_minors;
}
snprintf(rd_dev->name, ARRAY_SIZE(rd_dev->name), "%s",
dev_name);
rd_dev->dev = device_create(ramdump_class, parent,
MKDEV(MAJOR(ramdump_dev), minor),
rd_dev, rd_dev->name);
if (IS_ERR(rd_dev->dev)) {
ret = PTR_ERR(rd_dev->dev);
pr_err("%s: device_create failed for %s (%d)\n", __func__,
dev_name, ret);
goto fail_return_minor;
}
cdev_init(&rd_dev->cdev, NULL);
ret = cdev_add(&rd_dev->cdev, MKDEV(MAJOR(ramdump_dev), minor), 1);
if (ret) {
pr_err("%s: cdev_add failed for %s (%d)\n", __func__,
dev_name, ret);
goto fail_cdev_add;
}
return (void *)rd_dev->dev;
fail_cdev_add:
device_unregister(rd_dev->dev);
fail_return_minor:
ida_simple_remove(&rd_minor_id, minor);
fail_out_of_minors:
kfree(rd_dev);
return ERR_PTR(ret);
}
EXPORT_SYMBOL(qcom_create_ramdump_device);
void qcom_destroy_ramdump_device(void *dev)
{
struct ramdump_device *rd_dev = container_of(dev, struct ramdump_device, dev);
int minor = MINOR(rd_dev->cdev.dev);
if (IS_ERR_OR_NULL(rd_dev))
return;
cdev_del(&rd_dev->cdev);
device_unregister(rd_dev->dev);
ida_simple_remove(&rd_minor_id, minor);
kfree(rd_dev);
}
EXPORT_SYMBOL(qcom_destroy_ramdump_device);
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Ramdump driver");
MODULE_LICENSE("GPL v2");

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _QCOM_RAMDUMP_HEADER
@ -18,11 +19,21 @@ struct qcom_dump_segment {
};
#if IS_ENABLED(CONFIG_QCOM_RAMDUMP)
extern void *qcom_create_ramdump_device(const char *dev_name, struct device *parent);
extern void qcom_destroy_ramdump_device(void *dev);
extern int qcom_elf_dump(struct list_head *segs, struct device *dev, unsigned char class);
extern int qcom_dump(struct list_head *head, struct device *dev);
extern int qcom_fw_elf_dump(struct firmware *fw, struct device *dev);
extern bool dump_enabled(void);
#else
static inline void *qcom_create_ramdump_device(const char *dev_name,
struct device *parent)
{
return NULL;
}
static inline void qcom_destroy_ramdump_device(void *dev)
{
}
static inline int qcom_elf_dump(struct list_head *segs, struct device *dev, unsigned char class)
{
return -ENODEV;