From 465231e61638f51329cc62f2ad50447fdc375895 Mon Sep 17 00:00:00 2001 From: Lingutla Chandrasekhar Date: Mon, 13 Aug 2018 17:07:14 +0530 Subject: [PATCH] Minidump: Add update region support for minidump regions Add support to update client's entry physical/virtual addresses, which is useful for DLKM, dynamic address changing clients. Currently, msm_minidump_add_region() returns 0 on success, instead of that, return 'region number' of the entry in the minidump table. So clients who wants to update their address in future, needs to book keep their entry in minidump table. Change-Id: I03abbbd87bf6d2e11cf637525412d352772821cf Signed-off-by: Lingutla Chandrasekhar Signed-off-by: Neeraj Upadhyay --- drivers/net/wireless/cnss2/main.c | 2 +- drivers/soc/qcom/dcc_v2.c | 2 +- drivers/soc/qcom/memory_dump_v2.c | 4 +- drivers/soc/qcom/minidump_log.c | 10 ++-- drivers/soc/qcom/msm_minidump.c | 81 ++++++++++++++++++++++++++++--- drivers/soc/qcom/qcom_wdt_core.c | 2 +- include/soc/qcom/minidump.h | 15 ++++-- kernel/trace/msm_rtb.c | 2 +- 8 files changed, 98 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 44dad7fbcd55..2db4baecb307 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -2183,7 +2183,7 @@ int cnss_minidump_add_region(struct cnss_plat_data *plat_priv, md_entry.name, va, &pa, size); ret = msm_minidump_add_region(&md_entry); - if (ret) + if (ret < 0) cnss_pr_err("Failed to add mini dump region, err = %d\n", ret); return ret; diff --git a/drivers/soc/qcom/dcc_v2.c b/drivers/soc/qcom/dcc_v2.c index 989d7def9fc1..0f71cbb3129b 100644 --- a/drivers/soc/qcom/dcc_v2.c +++ b/drivers/soc/qcom/dcc_v2.c @@ -1849,7 +1849,7 @@ static int dcc_probe(struct platform_device *pdev) md_entry.virt_addr = (uintptr_t)drvdata->ram_base; md_entry.phys_addr = res->start; md_entry.size = drvdata->ram_size; - if (msm_minidump_add_region(&md_entry)) + if (msm_minidump_add_region(&md_entry) < 0) dev_err(drvdata->dev, "Failed to add DCC data in Minidump\n"); return 0; diff --git a/drivers/soc/qcom/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c index a56362923111..7bf0f4177428 100644 --- a/drivers/soc/qcom/memory_dump_v2.c +++ b/drivers/soc/qcom/memory_dump_v2.c @@ -662,7 +662,7 @@ int msm_dump_data_register(enum msm_dump_table_ids id, ret = register_dump_table_entry(id, entry); if (!ret) - if (msm_dump_data_add_minidump(entry)) + if (msm_dump_data_add_minidump(entry) < 0) pr_err("Failed to add entry in Minidump table\n"); return ret; @@ -856,7 +856,7 @@ static int mem_dump_alloc(struct platform_device *pdev) md_entry.size = size; md_entry.id = id; strlcpy(md_entry.name, child_node->name, sizeof(md_entry.name)); - if (msm_minidump_add_region(&md_entry)) + if (msm_minidump_add_region(&md_entry) < 0) dev_err(&pdev->dev, "Mini dump entry failed id = %d\n", id); diff --git a/drivers/soc/qcom/minidump_log.c b/drivers/soc/qcom/minidump_log.c index aa99b2dac9f1..72118b7f1154 100644 --- a/drivers/soc/qcom/minidump_log.c +++ b/drivers/soc/qcom/minidump_log.c @@ -33,7 +33,7 @@ static void __init register_log_buf(void) md_entry.virt_addr = (uintptr_t) (*log_bufp); md_entry.phys_addr = virt_to_phys(*log_bufp); md_entry.size = *log_buf_lenp; - if (msm_minidump_add_region(&md_entry)) + if (msm_minidump_add_region(&md_entry) < 0) pr_err("Failed to add logbuf in Minidump\n"); } @@ -52,7 +52,7 @@ static void register_stack_entry(struct md_region *ksp_entry, u64 sp, u64 size, ksp_entry->phys_addr = virt_to_phys((uintptr_t *)sp); } - if (msm_minidump_add_region(ksp_entry)) + if (msm_minidump_add_region(ksp_entry) < 0) pr_err("Failed to add stack of cpu %d in Minidump\n", cpu); } @@ -68,7 +68,7 @@ static void __init register_kernel_sections(void) ksec_entry.virt_addr = (uintptr_t)_sdata; ksec_entry.phys_addr = virt_to_phys(_sdata); ksec_entry.size = roundup((__bss_stop - _sdata), 4); - if (msm_minidump_add_region(&ksec_entry)) + if (msm_minidump_add_region(&ksec_entry) < 0) pr_err("Failed to add data section in Minidump\n"); /* Add percpu static sections */ @@ -81,7 +81,7 @@ static void __init register_kernel_sections(void) ksec_entry.virt_addr = (uintptr_t)start; ksec_entry.phys_addr = per_cpu_ptr_to_phys(start); ksec_entry.size = static_size; - if (msm_minidump_add_region(&ksec_entry)) + if (msm_minidump_add_region(&ksec_entry) < 0) pr_err("Failed to add percpu sections in Minidump\n"); } } @@ -153,7 +153,7 @@ void dump_stack_minidump(u64 sp) ktsk_entry.virt_addr = (u64)current; ktsk_entry.phys_addr = virt_to_phys((uintptr_t *)current); ktsk_entry.size = sizeof(struct task_struct); - if (msm_minidump_add_region(&ktsk_entry)) + if (msm_minidump_add_region(&ktsk_entry) < 0) pr_err("Failed to add current task %d in Minidump\n", cpu); } diff --git a/drivers/soc/qcom/msm_minidump.c b/drivers/soc/qcom/msm_minidump.c index d3ec9e9f28ab..3ed713753f1c 100644 --- a/drivers/soc/qcom/msm_minidump.c +++ b/drivers/soc/qcom/msm_minidump.c @@ -34,7 +34,7 @@ struct md_table { struct md_ss_toc *md_ss_toc; struct md_global_toc *md_gbl_toc; struct md_ss_region *md_regions; - struct md_region entry[MAX_NUM_ENTRIES]; + struct md_region entry[MAX_NUM_ENTRIES]; }; /** @@ -55,8 +55,11 @@ struct md_elfhdr { /* Protect elfheader and smem table from deferred calls contention */ static DEFINE_SPINLOCK(mdt_lock); +static DEFINE_RWLOCK(mdt_remove_lock); static struct md_table minidump_table; static struct md_elfhdr minidump_elfheader; +static int first_removed_entry = INT_MAX; +static bool md_init_done; /* Number of pending entries to be added in ToC regions */ static unsigned int pendings; @@ -166,11 +169,8 @@ bool msm_minidump_enabled(void) } EXPORT_SYMBOL(msm_minidump_enabled); -int msm_minidump_add_region(const struct md_region *entry) +static inline int validate_region(const struct md_region *entry) { - u32 entries; - struct md_region *mdr; - if (!entry) return -EINVAL; @@ -180,6 +180,68 @@ int msm_minidump_add_region(const struct md_region *entry) return -EINVAL; } + return 0; +} + +int msm_minidump_update_region(int regno, const struct md_region *entry) +{ + int ret = 0; + struct md_region *mdr; + struct md_ss_region *mdssr; + struct elfhdr *hdr = minidump_elfheader.ehdr; + struct elf_shdr *shdr; + struct elf_phdr *phdr; + + /* Ensure that init completes before we update regions */ + if (!smp_load_acquire(&md_init_done)) + return -EINVAL; + + if (validate_region(entry) || (regno >= MAX_NUM_ENTRIES)) + return -EINVAL; + + read_lock(&mdt_remove_lock); + + if (regno >= first_removed_entry) { + pr_err("Region:[%s] was moved\n", entry->name); + ret = -EINVAL; + goto err_unlock; + } + + if (md_entry_num(entry) < 0) { + pr_err("Region:[%s] does not exist to update.\n", entry->name); + ret = -ENOMEM; + goto err_unlock; + } + + mdr = &minidump_table.entry[regno]; + mdr->virt_addr = entry->virt_addr; + mdr->phys_addr = entry->phys_addr; + + mdssr = &minidump_table.md_regions[regno + 1]; + mdssr->region_base_address = entry->phys_addr; + + shdr = elf_section(hdr, regno + 4); + phdr = elf_program(hdr, regno + 1); + + shdr->sh_addr = (elf_addr_t)entry->virt_addr; + phdr->p_vaddr = entry->virt_addr; + phdr->p_paddr = entry->phys_addr; + +err_unlock: + read_unlock(&mdt_remove_lock); + + return ret; +} +EXPORT_SYMBOL(msm_minidump_update_region); + +int msm_minidump_add_region(const struct md_region *entry) +{ + u32 entries; + struct md_region *mdr; + + if (validate_region(entry)) + return -EINVAL; + spin_lock(&mdt_lock); if (md_entry_num(entry) >= 0) { pr_err("Entry name already exist.\n"); @@ -212,7 +274,7 @@ int msm_minidump_add_region(const struct md_region *entry) spin_unlock(&mdt_lock); - return 0; + return entries; } EXPORT_SYMBOL(msm_minidump_add_region); @@ -301,6 +363,7 @@ int msm_minidump_remove_region(const struct md_region *entry) return -EINVAL; spin_lock(&mdt_lock); + write_lock(&mdt_remove_lock); ecount = minidump_table.num_regions; rcount = minidump_table.md_ss_toc->ss_region_count; rgno = md_entry_num(entry); @@ -309,6 +372,8 @@ int msm_minidump_remove_region(const struct md_region *entry) goto out; } + if (first_removed_entry > rgno) + first_removed_entry = rgno; minidump_table.md_ss_toc->md_ss_toc_init = 0; /* Remove entry from: entry list, ss region list and elf header */ @@ -338,9 +403,11 @@ int msm_minidump_remove_region(const struct md_region *entry) minidump_table.md_ss_toc->md_ss_toc_init = 1; minidump_table.num_regions--; + write_unlock(&mdt_remove_lock); spin_unlock(&mdt_lock); return 0; out: + write_unlock(&mdt_remove_lock); spin_unlock(&mdt_lock); pr_err("Minidump is broken..disable Minidump collection\n"); return -EINVAL; @@ -507,6 +574,8 @@ static int __init msm_minidump_init(void) pr_info("Enabled with max number of regions %d\n", CONFIG_MINIDUMP_MAX_ENTRIES); + /* All updates above should be visible, before init completes */ + smp_store_release(&md_init_done, true); return 0; } subsys_initcall(msm_minidump_init) diff --git a/drivers/soc/qcom/qcom_wdt_core.c b/drivers/soc/qcom/qcom_wdt_core.c index 077a7ab45aed..f21ec0572096 100644 --- a/drivers/soc/qcom/qcom_wdt_core.c +++ b/drivers/soc/qcom/qcom_wdt_core.c @@ -573,7 +573,7 @@ int qcom_wdt_register(struct platform_device *pdev, md_entry.virt_addr = (uintptr_t)wdog_dd; md_entry.phys_addr = virt_to_phys(wdog_dd); md_entry.size = sizeof(*wdog_dd); - if (msm_minidump_add_region(&md_entry)) + if (msm_minidump_add_region(&md_entry) < 0) dev_err(wdog_dd->dev, "Failed to add Wdt data in Minidump\n"); return 0; err: diff --git a/include/soc/qcom/minidump.h b/include/soc/qcom/minidump.h index 46ed6b8b7341..b6673b84ff5b 100644 --- a/include/soc/qcom/minidump.h +++ b/include/soc/qcom/minidump.h @@ -25,14 +25,23 @@ struct md_region { u64 size; }; -/* Register an entry in Minidump table +/* + * Register an entry in Minidump table * Returns: - * Zero: on successful addition - * Negetive error number on failures + * region number: entry position in minidump table. + * Negative error number on failures. */ #if IS_ENABLED(CONFIG_QCOM_MINIDUMP) extern int msm_minidump_add_region(const struct md_region *entry); extern int msm_minidump_remove_region(const struct md_region *entry); +/* + * Update registered region address in Minidump table. + * It does not hold any locks, so strictly serialize the region updates. + * Returns: + * Zero: on successfully update + * Negetive error number on failures. + */ +extern int msm_minidump_update_region(int regno, const struct md_region *entry); extern bool msm_minidump_enabled(void); extern void dump_stack_minidump(u64 sp); #else diff --git a/kernel/trace/msm_rtb.c b/kernel/trace/msm_rtb.c index d08115db0ff3..c6bc2b3987e8 100644 --- a/kernel/trace/msm_rtb.c +++ b/kernel/trace/msm_rtb.c @@ -290,7 +290,7 @@ static int msm_rtb_probe(struct platform_device *pdev) md_entry.virt_addr = (uintptr_t)msm_rtb.rtb; md_entry.phys_addr = msm_rtb.phys; md_entry.size = msm_rtb.size; - if (msm_minidump_add_region(&md_entry)) + if (msm_minidump_add_region(&md_entry) < 0) pr_info("Failed to add RTB in Minidump\n"); #if defined(CONFIG_QCOM_RTB_SEPARATE_CPUS)