From 8aad5e04edbc219f4e0e452aa92b0171355cb7d8 Mon Sep 17 00:00:00 2001 From: Tingwei Zhang Date: Mon, 2 Sep 2019 10:57:02 +0800 Subject: [PATCH] soc: qcom: Add snapshot of the memory dump v2 driver Add memory dump v2 driver from msm-4.19 commit 034b7f3bfb2c ("msm: cvp: Fix page fault when open 16 cvp sessions"). Remove dmac_flush_range since it's deprecated. Change-Id: Ibcc42c0d737abbb304a222ac51cfdd94019af534 Signed-off-by: Tingwei Zhang --- drivers/soc/qcom/Kconfig | 8 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/memory_dump_v2.c | 331 ++++++++++++++++++++++++++++++ include/soc/qcom/memory_dump.h | 134 ++++++++++++ 4 files changed, 474 insertions(+) create mode 100644 drivers/soc/qcom/memory_dump_v2.c create mode 100644 include/soc/qcom/memory_dump.h diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index cc497ebd393e..09f34053eddc 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -269,6 +269,14 @@ config QCOM_SMD_RPM Say M here if you want to include support for the Qualcomm RPM as a module. This will build a module called "qcom-smd-rpm". +config QCOM_MEMORY_DUMP_V2 + bool "QCOM Memory Dump V2 Support" + help + This enables memory dump feature. It allows various client + subsystems to register respective dump regions. At the time + of deadlocks or cpu hangs these dump regions are captured to + give a snapshot of the system at the time of the crash. + config QCOM_SMEM_STATE bool diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 7142bff8dc36..0720c5237fbb 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_QCOM_IPCC) += qcom_ipcc.o obj-$(CONFIG_QCOM_APR) += apr.o obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o obj-$(CONFIG_QCOM_LAHAINA_LLCC) += llcc-lahaina.o +obj-$(CONFIG_QCOM_MEMORY_DUMP_V2) += memory_dump_v2.o obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o diff --git a/drivers/soc/qcom/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c new file mode 100644 index 000000000000..49f93c9b604c --- /dev/null +++ b/drivers/soc/qcom/memory_dump_v2.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2017, 2019, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSM_DUMP_TABLE_VERSION MSM_DUMP_MAKE_VERSION(2, 0) + +#define SCM_CMD_DEBUG_LAR_UNLOCK 0x4 + +struct msm_dump_table { + uint32_t version; + uint32_t num_entries; + struct msm_dump_entry entries[MAX_NUM_ENTRIES]; +}; + +struct msm_memory_dump { + uint64_t table_phys; + struct msm_dump_table *table; +}; + +static struct msm_memory_dump memdump; + +uint32_t msm_dump_table_version(void) +{ + return MSM_DUMP_TABLE_VERSION; +} +EXPORT_SYMBOL(msm_dump_table_version); + +static int msm_dump_table_register(struct msm_dump_entry *entry) +{ + struct msm_dump_entry *e; + struct msm_dump_table *table = memdump.table; + + if (!table || table->num_entries >= MAX_NUM_ENTRIES) + return -EINVAL; + + e = &table->entries[table->num_entries]; + e->id = entry->id; + e->type = MSM_DUMP_TYPE_TABLE; + e->addr = entry->addr; + table->num_entries++; + + return 0; +} + +static struct msm_dump_table *msm_dump_get_table(enum msm_dump_table_ids id) +{ + struct msm_dump_table *table = memdump.table; + int i; + + if (!table) { + pr_err("mem dump base table does not exist\n"); + return ERR_PTR(-EINVAL); + } + + for (i = 0; i < MAX_NUM_ENTRIES; i++) { + if (table->entries[i].id == id) + break; + } + if (i == MAX_NUM_ENTRIES || !table->entries[i].addr) { + pr_err("mem dump base table entry %d invalid\n", id); + return ERR_PTR(-EINVAL); + } + + /* Get the apps table pointer */ + table = phys_to_virt(table->entries[i].addr); + + return table; +} + +static int msm_dump_data_add_minidump(struct msm_dump_entry *entry) +{ + struct msm_dump_data *data; + struct md_region md_entry; + + data = (struct msm_dump_data *)(phys_to_virt(entry->addr)); + + if (!data->addr || !data->len) + return -EINVAL; + + if (!strcmp(data->name, "")) { + pr_debug("Entry name is NULL, Use ID %d for minidump\n", + entry->id); + snprintf(md_entry.name, sizeof(md_entry.name), "KMDT0x%X", + entry->id); + } else { + strlcpy(md_entry.name, data->name, sizeof(md_entry.name)); + } + + md_entry.phys_addr = data->addr; + md_entry.virt_addr = (uintptr_t)phys_to_virt(data->addr); + md_entry.size = data->len; + md_entry.id = entry->id; + + return msm_minidump_add_region(&md_entry); +} + +static int register_dump_table_entry(enum msm_dump_table_ids id, + struct msm_dump_entry *entry) +{ + struct msm_dump_entry *e; + struct msm_dump_table *table; + + table = msm_dump_get_table(id); + if (IS_ERR(table)) + return PTR_ERR(table); + + if (!table || table->num_entries >= MAX_NUM_ENTRIES) + return -EINVAL; + + e = &table->entries[table->num_entries]; + e->id = entry->id; + e->type = MSM_DUMP_TYPE_DATA; + e->addr = entry->addr; + table->num_entries++; + + return 0; +} + +/** + * msm_dump_data_register - register to dump data and minidump framework + * @id: ID of the dump table. + * @entry: dump entry to be registered + * This api will register the entry passed to dump table and minidump table + */ +int msm_dump_data_register(enum msm_dump_table_ids id, + struct msm_dump_entry *entry) +{ + int ret; + + ret = register_dump_table_entry(id, entry); + if (!ret) + if (msm_dump_data_add_minidump(entry)) + pr_err("Failed to add entry in Minidump table\n"); + + return ret; +} +EXPORT_SYMBOL(msm_dump_data_register); + +/** + * msm_dump_data_register_nominidump - register to dump data framework + * @id: ID of the dump table. + * @entry: dump entry to be registered + * This api will register the entry passed to dump table only + */ +int msm_dump_data_register_nominidump(enum msm_dump_table_ids id, + struct msm_dump_entry *entry) +{ + return register_dump_table_entry(id, entry); +} +EXPORT_SYMBOL(msm_dump_data_register_nominidump); + +static int __init init_memory_dump(void) +{ + struct msm_dump_table *table; + struct msm_dump_entry entry; + struct device_node *np; + void __iomem *imem_base; + int ret; + + np = of_find_compatible_node(NULL, NULL, + "qcom,msm-imem-mem_dump_table"); + if (!np) { + pr_err("mem dump base table DT node does not exist\n"); + return -ENODEV; + } + + imem_base = of_iomap(np, 0); + if (!imem_base) { + pr_err("mem dump base table imem offset mapping failed\n"); + return -ENOMEM; + } + + memdump.table = kzalloc(sizeof(struct msm_dump_table), GFP_KERNEL); + if (!memdump.table) { + ret = -ENOMEM; + goto err0; + } + memdump.table->version = MSM_DUMP_TABLE_VERSION; + memdump.table_phys = virt_to_phys(memdump.table); + memcpy_toio(imem_base, &memdump.table_phys, sizeof(memdump.table_phys)); + /* Ensure write to imem_base is complete before unmapping */ + mb(); + pr_info("MSM Memory Dump base table set up\n"); + + iounmap(imem_base); + + table = kzalloc(sizeof(struct msm_dump_table), GFP_KERNEL); + if (!table) { + ret = -ENOMEM; + goto err1; + } + table->version = MSM_DUMP_TABLE_VERSION; + + entry.id = MSM_DUMP_TABLE_APPS; + entry.addr = virt_to_phys(table); + ret = msm_dump_table_register(&entry); + if (ret) { + pr_info("mem dump apps data table register failed\n"); + goto err2; + } + pr_info("MSM Memory Dump apps data table set up\n"); + + return 0; +err2: + kfree(table); +err1: + kfree(memdump.table); + return ret; +err0: + iounmap(imem_base); + return ret; +} +early_initcall(init_memory_dump); + +#ifdef CONFIG_MSM_DEBUG_LAR_UNLOCK +static int __init init_debug_lar_unlock(void) +{ + int ret; + uint32_t argument = 0; + struct scm_desc desc = {0}; + + if (!is_scm_armv8()) + ret = scm_call(SCM_SVC_TZ, SCM_CMD_DEBUG_LAR_UNLOCK, &argument, + sizeof(argument), NULL, 0); + else + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_TZ, + SCM_CMD_DEBUG_LAR_UNLOCK), &desc); + if (ret) + pr_err("Core Debug Lock unlock failed, ret: %d\n", ret); + else + pr_info("Core Debug Lock unlocked\n"); + + return ret; +} +early_initcall(init_debug_lar_unlock); +#endif + +static int mem_dump_probe(struct platform_device *pdev) +{ + struct device_node *child_node; + const struct device_node *node = pdev->dev.of_node; + static dma_addr_t dump_addr; + static void *dump_vaddr; + struct msm_dump_data *dump_data; + struct msm_dump_entry dump_entry; + int ret; + u32 size, id; + + for_each_available_child_of_node(node, child_node) { + ret = of_property_read_u32(child_node, "qcom,dump-size", &size); + if (ret) { + dev_err(&pdev->dev, "Unable to find size for %s\n", + child_node->name); + continue; + } + + ret = of_property_read_u32(child_node, "qcom,dump-id", &id); + if (ret) { + dev_err(&pdev->dev, "Unable to find id for %s\n", + child_node->name); + continue; + } + + dump_vaddr = dma_alloc_coherent(&pdev->dev, size, + &dump_addr, GFP_KERNEL); + + if (!dump_vaddr) { + dev_err(&pdev->dev, "Couldn't get memory for dumping\n"); + continue; + } + + dump_data = devm_kzalloc(&pdev->dev, + sizeof(struct msm_dump_data), GFP_KERNEL); + if (!dump_data) { + dma_free_coherent(&pdev->dev, size, dump_vaddr, + dump_addr); + continue; + } + + dump_data->addr = dump_addr; + dump_data->len = size; + dump_entry.id = id; + strlcpy(dump_data->name, child_node->name, + sizeof(dump_data->name)); + dump_entry.addr = virt_to_phys(dump_data); + ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); + if (ret) { + dev_err(&pdev->dev, "Data dump setup failed, id = %d\n", + id); + dma_free_coherent(&pdev->dev, size, dump_vaddr, + dump_addr); + devm_kfree(&pdev->dev, dump_data); + } + } + return 0; +} + +static const struct of_device_id mem_dump_match_table[] = { + {.compatible = "qcom,mem-dump",}, + {} +}; + +static struct platform_driver mem_dump_driver = { + .probe = mem_dump_probe, + .driver = { + .name = "msm_mem_dump", + .of_match_table = mem_dump_match_table, + }, +}; + +static int __init mem_dump_init(void) +{ + return platform_driver_register(&mem_dump_driver); +} + +pure_initcall(mem_dump_init); diff --git a/include/soc/qcom/memory_dump.h b/include/soc/qcom/memory_dump.h new file mode 100644 index 000000000000..bc84bc8d8ef8 --- /dev/null +++ b/include/soc/qcom/memory_dump.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012, 2014-2017, 2019, The Linux Foundation. All rights reserved. + */ + +#ifndef __MSM_MEMORY_DUMP_H +#define __MSM_MEMORY_DUMP_H + +#include + +enum dump_client_type { + MSM_CPU_CTXT = 0, + MSM_L1_CACHE, + MSM_L2_CACHE, + MSM_OCMEM, + MSM_TMC_ETFETB, + MSM_ETM0_REG, + MSM_ETM1_REG, + MSM_ETM2_REG, + MSM_ETM3_REG, + MSM_TMC0_REG, /* TMC_ETR */ + MSM_TMC1_REG, /* TMC_ETF */ + MSM_LOG_BUF, + MSM_LOG_BUF_FIRST_IDX, + MAX_NUM_CLIENTS, +}; + +struct msm_client_dump { + enum dump_client_type id; + unsigned long start_addr; + unsigned long end_addr; +}; + +#ifdef CONFIG_QCOM_MEMORY_DUMP +extern int msm_dump_tbl_register(struct msm_client_dump *client_entry); +#else +static inline int msm_dump_tbl_register(struct msm_client_dump *entry) +{ + return -EIO; +} +#endif + + +#if defined(CONFIG_QCOM_MEMORY_DUMP) || defined(CONFIG_QCOM_MEMORY_DUMP_V2) +extern uint32_t msm_dump_table_version(void); +#else +static inline uint32_t msm_dump_table_version(void) +{ + return 0; +} +#endif + +#define MSM_DUMP_MAKE_VERSION(ma, mi) ((ma << 20) | mi) +#define MSM_DUMP_MAJOR(val) (val >> 20) +#define MSM_DUMP_MINOR(val) (val & 0xFFFFF) + + +#define MAX_NUM_ENTRIES 0x150 + +enum msm_dump_data_ids { + MSM_DUMP_DATA_CPU_CTX = 0x00, + MSM_DUMP_DATA_L1_INST_CACHE = 0x60, + MSM_DUMP_DATA_L1_DATA_CACHE = 0x80, + MSM_DUMP_DATA_ETM_REG = 0xA0, + MSM_DUMP_DATA_L2_CACHE = 0xC0, + MSM_DUMP_DATA_L3_CACHE = 0xD0, + MSM_DUMP_DATA_OCMEM = 0xE0, + MSM_DUMP_DATA_CNSS_WLAN = 0xE1, + MSM_DUMP_DATA_WIGIG = 0xE2, + MSM_DUMP_DATA_PMIC = 0xE4, + MSM_DUMP_DATA_DBGUI_REG = 0xE5, + MSM_DUMP_DATA_DCC_REG = 0xE6, + MSM_DUMP_DATA_DCC_SRAM = 0xE7, + MSM_DUMP_DATA_MISC = 0xE8, + MSM_DUMP_DATA_VSENSE = 0xE9, + MSM_DUMP_DATA_RPM = 0xEA, + MSM_DUMP_DATA_SCANDUMP = 0xEB, + MSM_DUMP_DATA_RPMH = 0xEC, + MSM_DUMP_DATA_TMC_ETF = 0xF0, + MSM_DUMP_DATA_TMC_ETF_SWAO = 0xF1, + MSM_DUMP_DATA_TMC_REG = 0x100, + MSM_DUMP_DATA_TMC_ETF_SWAO_REG = 0x102, + MSM_DUMP_DATA_LOG_BUF = 0x110, + MSM_DUMP_DATA_LOG_BUF_FIRST_IDX = 0x111, + MSM_DUMP_DATA_SCANDUMP_PER_CPU = 0x130, + MSM_DUMP_DATA_LLCC_PER_INSTANCE = 0x140, + MSM_DUMP_DATA_MAX = MAX_NUM_ENTRIES, +}; + +enum msm_dump_table_ids { + MSM_DUMP_TABLE_APPS, + MSM_DUMP_TABLE_MAX = MAX_NUM_ENTRIES, +}; + +enum msm_dump_type { + MSM_DUMP_TYPE_DATA, + MSM_DUMP_TYPE_TABLE, +}; + +struct msm_dump_data { + uint32_t version; + uint32_t magic; + char name[32]; + uint64_t addr; + uint64_t len; + uint32_t reserved; +}; + +struct msm_dump_entry { + uint32_t id; + char name[32]; + uint32_t type; + uint64_t addr; +}; + +#ifdef CONFIG_QCOM_MEMORY_DUMP_V2 +extern int msm_dump_data_register(enum msm_dump_table_ids id, + struct msm_dump_entry *entry); +extern int msm_dump_data_register_nominidump(enum msm_dump_table_ids id, + struct msm_dump_entry *entry); +#else +static inline int msm_dump_data_register(enum msm_dump_table_ids id, + struct msm_dump_entry *entry) +{ + return -EINVAL; +} +static inline int msm_dump_data_register_nominidump(enum msm_dump_table_ids id, + struct msm_dump_entry *entry) +{ + return -EINVAL; +} +#endif + +#endif