diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index 610464f02ef6..64fed5117f51 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -1842,7 +1842,7 @@ static int lpm_probe(struct platform_device *pdev) md_entry.phys_addr = lpm_debug_phys; md_entry.size = size; md_entry.id = MINIDUMP_DEFAULT_ID; - if (msm_minidump_add_region(&md_entry)) + if (msm_minidump_add_region(&md_entry) < 0) pr_info("Failed to add lpm_debug in Minidump\n"); return 0; diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 87a8520eac1f..ea3444de8017 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -1897,7 +1897,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/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c index 59d164be6c79..a82fba07a8c2 100644 --- a/drivers/soc/qcom/memory_dump_v2.c +++ b/drivers/soc/qcom/memory_dump_v2.c @@ -679,7 +679,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; @@ -876,7 +876,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 c0ef1be1e1fd..7602dd8a4b3c 100644 --- a/drivers/soc/qcom/minidump_log.c +++ b/drivers/soc/qcom/minidump_log.c @@ -34,7 +34,7 @@ static void __init register_log_buf(void) md_entry.phys_addr = virt_to_phys(*log_bufp); md_entry.size = *log_buf_lenp; md_entry.id = MINIDUMP_DEFAULT_ID; - if (msm_minidump_add_region(&md_entry)) + if (msm_minidump_add_region(&md_entry) < 0) pr_err("Failed to add logbuf in Minidump\n"); } @@ -54,7 +54,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); } @@ -71,7 +71,7 @@ static void __init register_kernel_sections(void) ksec_entry.phys_addr = virt_to_phys(_sdata); ksec_entry.size = roundup((__bss_stop - _sdata), 4); ksec_entry.id = MINIDUMP_DEFAULT_ID; - 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 */ @@ -85,7 +85,7 @@ static void __init register_kernel_sections(void) ksec_entry.phys_addr = per_cpu_ptr_to_phys(start); ksec_entry.size = static_size; ksec_entry.id = MINIDUMP_DEFAULT_ID; - 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"); } } @@ -158,7 +158,7 @@ void dump_stack_minidump(u64 sp) ktsk_entry.phys_addr = virt_to_phys((uintptr_t *)current); ktsk_entry.size = sizeof(struct task_struct); ktsk_entry.id = MINIDUMP_DEFAULT_ID; - 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 7bd49512da21..4e37b306dd76 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,10 @@ 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; /* Number of pending entries to be added in ToC regions */ static unsigned int pendings; @@ -166,11 +168,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 +179,64 @@ 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; + + 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 +269,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 +358,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 +367,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 +398,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; diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c index 301d06f0f8cf..b840baca219e 100644 --- a/drivers/soc/qcom/watchdog_v2.c +++ b/drivers/soc/qcom/watchdog_v2.c @@ -803,7 +803,7 @@ static void minidump_reg_init_log_buf(void) md_entry.phys_addr = log_buf_paddr; md_entry.size = *log_buf_size; md_entry.id = MINIDUMP_DEFAULT_ID; - if (msm_minidump_add_region(&md_entry)) + if (msm_minidump_add_region(&md_entry) < 0) pr_err("Failed to add init_log_buf in Minidump\n"); } @@ -1049,7 +1049,7 @@ static int msm_watchdog_probe(struct platform_device *pdev) md_entry.phys_addr = virt_to_phys(wdog_dd); md_entry.size = sizeof(*wdog_dd); md_entry.id = MINIDUMP_DEFAULT_ID; - if (msm_minidump_add_region(&md_entry)) + if (msm_minidump_add_region(&md_entry) < 0) pr_info("Failed to add Watchdog data in Minidump\n"); return 0; diff --git a/include/soc/qcom/minidump.h b/include/soc/qcom/minidump.h index 5a7e66d61708..50def3018321 100644 --- a/include/soc/qcom/minidump.h +++ b/include/soc/qcom/minidump.h @@ -27,14 +27,23 @@ struct md_region { u64 size; }; -/* Register an entry in Minidump table - * Returns: - * Zero: on successful addition - * Negetive error number on failures - */ #ifdef CONFIG_QCOM_MINIDUMP +/* + * Register an entry in Minidump table + * Returns: + * region number: entry position in minidump table. + * Negetive error number on failures. + */ 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 45dbdbca024d..1d285cc8863b 100644 --- a/kernel/trace/msm_rtb.c +++ b/kernel/trace/msm_rtb.c @@ -292,7 +292,7 @@ static int msm_rtb_probe(struct platform_device *pdev) md_entry.phys_addr = msm_rtb.phys; md_entry.size = msm_rtb.size; md_entry.id = MINIDUMP_DEFAULT_ID; - 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)