Merge "soc: qcom: Add SSR minidump provision for subsystem ramdump"
This commit is contained in:
commit
7392248e79
7 changed files with 291 additions and 4 deletions
|
@ -73,6 +73,11 @@ USB Diag Cookies:
|
|||
Memory region used to store USB PID and serial numbers to be used by
|
||||
bootloader in download mode.
|
||||
|
||||
SSR Minidump Offset
|
||||
-------------------
|
||||
-Compatible: "qcom,msm-imem-minidump"
|
||||
-reg: start address and size of ssr imem region
|
||||
|
||||
Required properties:
|
||||
-compatible: "qcom,msm-imem-diag-dload"
|
||||
-reg: start address and size of USB Diag download mode region in imem
|
||||
|
@ -121,4 +126,9 @@ Example:
|
|||
compatible = "qcom,msm-imem-emergency_download_mode";
|
||||
reg = <0xfe0 12>;
|
||||
};
|
||||
|
||||
ss_mdump@b88 {
|
||||
compatible = "qcom,msm-imem-minidump";
|
||||
reg = <0xb88 28>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -88,6 +88,7 @@ Optional properties:
|
|||
- qcom,override-acc-1: Override the default ACC settings with this value if present.
|
||||
- qcom,cx-ipeak-vote: Boolean- Present if we need to set bit 5 of cxip_lm_vote_clear
|
||||
during modem shutdown
|
||||
- qcom,minidump-id: Unique id for each subsystem
|
||||
|
||||
One child node to represent the MBA image may be specified, when the MBA image
|
||||
needs to be loaded in a specifically carved out memory region.
|
||||
|
|
|
@ -56,7 +56,9 @@
|
|||
#endif
|
||||
|
||||
#define PIL_NUM_DESC 10
|
||||
#define NUM_OF_ENCRYPTED_KEY 3
|
||||
static void __iomem *pil_info_base;
|
||||
static void __iomem *pil_minidump_base;
|
||||
|
||||
/**
|
||||
* proxy_timeout - Override for proxy vote timeouts
|
||||
|
@ -79,6 +81,18 @@ struct pil_mdt {
|
|||
struct elf32_phdr phdr[];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct boot_minidump_smem_region - Representation of SMEM TOC
|
||||
* @region_name: Name of modem segment to be dumped
|
||||
* @region_base_address: Where segment start from
|
||||
* @region_size: Size of segment to be dumped
|
||||
*/
|
||||
struct boot_minidump_smem_region {
|
||||
char region_name[16];
|
||||
u64 region_base_address;
|
||||
u64 region_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pil_seg - memory map representing one segment
|
||||
* @next: points to next seg mentor NULL if last segment
|
||||
|
@ -133,11 +147,67 @@ struct pil_priv {
|
|||
phys_addr_t region_end;
|
||||
void *region;
|
||||
struct pil_image_info __iomem *info;
|
||||
struct md_ssr_ss_info __iomem *minidump;
|
||||
int minidump_id;
|
||||
int id;
|
||||
int unvoted_flag;
|
||||
size_t region_size;
|
||||
};
|
||||
|
||||
static int pil_do_minidump(struct pil_desc *desc, void *ramdump_dev)
|
||||
{
|
||||
struct boot_minidump_smem_region __iomem *region_info;
|
||||
struct ramdump_segment *ramdump_segs, *s;
|
||||
struct pil_priv *priv = desc->priv;
|
||||
void __iomem *subsys_smem_base;
|
||||
void __iomem *offset;
|
||||
int ss_mdump_seg_cnt;
|
||||
int ret, i;
|
||||
|
||||
memcpy(&offset, &priv->minidump, sizeof(priv->minidump));
|
||||
offset = offset + sizeof(priv->minidump->md_ss_smem_regions_baseptr);
|
||||
/* There are 3 encryption keys which also need to be dumped */
|
||||
ss_mdump_seg_cnt = readb_relaxed(offset) +
|
||||
NUM_OF_ENCRYPTED_KEY;
|
||||
|
||||
subsys_smem_base = ioremap(__raw_readl(priv->minidump),
|
||||
ss_mdump_seg_cnt * sizeof(*region_info));
|
||||
region_info =
|
||||
(struct boot_minidump_smem_region __iomem *)subsys_smem_base;
|
||||
ramdump_segs = kcalloc(ss_mdump_seg_cnt,
|
||||
sizeof(*ramdump_segs), GFP_KERNEL);
|
||||
if (!ramdump_segs)
|
||||
return -ENOMEM;
|
||||
|
||||
if (desc->subsys_vmid > 0)
|
||||
ret = pil_assign_mem_to_linux(desc, priv->region_start,
|
||||
(priv->region_end - priv->region_start));
|
||||
|
||||
s = ramdump_segs;
|
||||
for (i = 0; i < ss_mdump_seg_cnt; i++) {
|
||||
memcpy(&offset, ®ion_info, sizeof(region_info));
|
||||
memcpy(&s->name, ®ion_info, sizeof(region_info));
|
||||
offset = offset + sizeof(region_info->region_name);
|
||||
s->address = __raw_readl(offset);
|
||||
offset = offset + sizeof(region_info->region_base_address);
|
||||
s->size = __raw_readl(offset);
|
||||
s++;
|
||||
region_info++;
|
||||
}
|
||||
ret = do_minidump(ramdump_dev, ramdump_segs, ss_mdump_seg_cnt);
|
||||
kfree(ramdump_segs);
|
||||
if (ret)
|
||||
pil_err(desc, "%s: Ramdump collection failed for subsys %s rc:%d\n",
|
||||
__func__, desc->name, ret);
|
||||
writel_relaxed(0, &priv->minidump->md_ss_smem_regions_baseptr);
|
||||
writeb_relaxed(1, &priv->minidump->md_ss_ssr_cause);
|
||||
|
||||
if (desc->subsys_vmid > 0)
|
||||
ret = pil_assign_mem_to_subsys(desc, priv->region_start,
|
||||
(priv->region_end - priv->region_start));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* pil_do_ramdump() - Ramdump an image
|
||||
* @desc: descriptor from pil_desc_init()
|
||||
|
@ -153,6 +223,9 @@ int pil_do_ramdump(struct pil_desc *desc, void *ramdump_dev)
|
|||
int count = 0, ret;
|
||||
struct ramdump_segment *ramdump_segs, *s;
|
||||
|
||||
if (priv->minidump && (__raw_readl(priv->minidump) > 0))
|
||||
return pil_do_minidump(desc, ramdump_dev);
|
||||
|
||||
list_for_each_entry(seg, &priv->segs, list)
|
||||
count++;
|
||||
|
||||
|
@ -1014,9 +1087,10 @@ bool is_timeout_disabled(void)
|
|||
int pil_desc_init(struct pil_desc *desc)
|
||||
{
|
||||
struct pil_priv *priv;
|
||||
int ret;
|
||||
void __iomem *addr;
|
||||
int ret, ss_imem_offset_mdump;
|
||||
char buf[sizeof(priv->info->name)];
|
||||
struct device_node *ofnode = desc->dev->of_node;
|
||||
|
||||
if (WARN(desc->ops->proxy_unvote && !desc->ops->proxy_vote,
|
||||
"Invalid proxy voting. Ignoring\n"))
|
||||
|
@ -1039,6 +1113,22 @@ int pil_desc_init(struct pil_desc *desc)
|
|||
strncpy(buf, desc->name, sizeof(buf));
|
||||
__iowrite32_copy(priv->info->name, buf, sizeof(buf) / 4);
|
||||
}
|
||||
if (of_property_read_u32(ofnode, "qcom,minidump-id",
|
||||
&priv->minidump_id))
|
||||
pr_debug("minidump-id not found for %s\n", desc->name);
|
||||
else {
|
||||
ss_imem_offset_mdump =
|
||||
sizeof(struct md_ssr_ss_info) * priv->minidump_id;
|
||||
if (pil_minidump_base) {
|
||||
/* Add 0x4 to get start of struct md_ssr_ss_info base
|
||||
* from struct md_ssr_toc for any subsystem,
|
||||
* struct md_ssr_ss_info is actually the pointer
|
||||
* of ToC in smem for any subsystem.
|
||||
*/
|
||||
addr = pil_minidump_base + ss_imem_offset_mdump + 0x4;
|
||||
priv->minidump = (struct md_ssr_ss_info __iomem *)addr;
|
||||
}
|
||||
}
|
||||
|
||||
ret = pil_parse_devicetree(desc);
|
||||
if (ret)
|
||||
|
@ -1148,6 +1238,20 @@ static int __init msm_pil_init(void)
|
|||
for (i = 0; i < resource_size(&res)/sizeof(u32); i++)
|
||||
writel_relaxed(0, pil_info_base + (i * sizeof(u32)));
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "qcom,msm-imem-minidump");
|
||||
if (!np) {
|
||||
pr_warn("pil: failed to find qcom,msm-imem-minidump node\n");
|
||||
goto out;
|
||||
} else {
|
||||
pil_minidump_base = of_iomap(np, 0);
|
||||
if (!pil_minidump_base) {
|
||||
pr_err("unable to map pil minidump imem offset\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < sizeof(struct md_ssr_toc)/sizeof(u32); i++)
|
||||
writel_relaxed(0, pil_minidump_base + (i * sizeof(u32)));
|
||||
writel_relaxed(1, pil_minidump_base);
|
||||
out:
|
||||
return register_pm_notifier(&pil_pm_notifier);
|
||||
}
|
||||
|
@ -1158,6 +1262,8 @@ static void __exit msm_pil_exit(void)
|
|||
unregister_pm_notifier(&pil_pm_notifier);
|
||||
if (pil_info_base)
|
||||
iounmap(pil_info_base);
|
||||
if (pil_minidump_base)
|
||||
iounmap(pil_minidump_base);
|
||||
}
|
||||
module_exit(msm_pil_exit);
|
||||
|
||||
|
|
|
@ -74,6 +74,34 @@ struct pil_image_info {
|
|||
__le32 size;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define MAX_NUM_OF_SS 3
|
||||
|
||||
/**
|
||||
* struct md_ssr_ss_info - Info in imem about smem ToC
|
||||
* @md_ss_smem_regions_baseptr: Start physical address of SMEM TOC
|
||||
* @md_ss_num_of_regions: number of segments that need to be dumped
|
||||
* @md_ss_encryption_status: status of encryption of segments
|
||||
* @md_ss_ssr_cause: ssr cause enum
|
||||
*/
|
||||
struct md_ssr_ss_info {
|
||||
u32 md_ss_smem_regions_baseptr;
|
||||
u8 md_ss_num_of_regions;
|
||||
u8 md_ss_encryption_status;
|
||||
u8 md_ss_ssr_cause;
|
||||
u8 reserved;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct md_ssr_toc - Wrapper of struct md_ssr_ss_info
|
||||
* @md_ssr_toc_init: flag to indicate to MSS SW about imem init done
|
||||
* @md_ssr_ss: Instance of struct md_ssr_ss_info for a subsystem
|
||||
*/
|
||||
struct md_ssr_toc /* Shared IMEM ToC struct */
|
||||
{
|
||||
u32 md_ssr_toc_init;
|
||||
struct md_ssr_ss_info md_ssr_ss[MAX_NUM_OF_SS];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pil_reset_ops - PIL operations
|
||||
* @init_image: prepare an image for authentication
|
||||
|
|
|
@ -78,7 +78,8 @@
|
|||
#define MSS_MAGIC 0XAABADEAD
|
||||
/* CX_IPEAK Parameters */
|
||||
#define CX_IPEAK_MSS BIT(5)
|
||||
|
||||
/* Timeout value for MBA boot when minidump is enabled */
|
||||
#define MBA_ENCRYPTION_TIMEOUT 3000
|
||||
enum scm_cmd {
|
||||
PAS_MEM_SETUP_CMD = 2,
|
||||
};
|
||||
|
@ -244,7 +245,12 @@ static int pil_msa_wait_for_mba_ready(struct q6v5_data *drv)
|
|||
struct device *dev = drv->desc.dev;
|
||||
int ret;
|
||||
u32 status;
|
||||
u64 val = is_timeout_disabled() ? 0 : pbl_mba_boot_timeout_ms * 1000;
|
||||
u64 val;
|
||||
|
||||
if (of_property_read_bool(dev->of_node, "qcom,minidump-id"))
|
||||
pbl_mba_boot_timeout_ms = MBA_ENCRYPTION_TIMEOUT;
|
||||
|
||||
val = is_timeout_disabled() ? 0 : pbl_mba_boot_timeout_ms * 1000;
|
||||
|
||||
/* Wait for PBL completion. */
|
||||
ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status,
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <linux/of.h>
|
||||
|
||||
#define RAMDUMP_WAIT_MSECS 120000
|
||||
#define MAX_STRTBL_SIZE 512
|
||||
#define MAX_NAME_LENGTH 16
|
||||
|
||||
struct ramdump_device {
|
||||
char name[256];
|
||||
|
@ -391,12 +393,143 @@ static int _do_ramdump(void *handle, struct ramdump_segment *segments,
|
|||
|
||||
}
|
||||
|
||||
static inline struct elf_shdr *elf_sheader(struct elfhdr *hdr)
|
||||
{
|
||||
return (struct elf_shdr *)((size_t)hdr + (size_t)hdr->e_shoff);
|
||||
}
|
||||
|
||||
static inline struct elf_shdr *elf_section(struct elfhdr *hdr, int idx)
|
||||
{
|
||||
return &elf_sheader(hdr)[idx];
|
||||
}
|
||||
|
||||
static inline char *elf_str_table(struct elfhdr *hdr)
|
||||
{
|
||||
if (hdr->e_shstrndx == SHN_UNDEF)
|
||||
return NULL;
|
||||
return (char *)hdr + elf_section(hdr, hdr->e_shstrndx)->sh_offset;
|
||||
}
|
||||
|
||||
static inline unsigned int set_section_name(const char *name,
|
||||
struct elfhdr *ehdr)
|
||||
{
|
||||
char *strtab = elf_str_table(ehdr);
|
||||
static int strtable_idx = 1;
|
||||
int idx, ret = 0;
|
||||
|
||||
idx = strtable_idx;
|
||||
if ((strtab == NULL) || (name == NULL))
|
||||
return 0;
|
||||
|
||||
ret = idx;
|
||||
idx += strlcpy((strtab + idx), name, MAX_NAME_LENGTH);
|
||||
strtable_idx = idx + 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _do_minidump(void *handle, struct ramdump_segment *segments,
|
||||
int nsegments)
|
||||
{
|
||||
int ret, i;
|
||||
struct ramdump_device *rd_dev = (struct ramdump_device *)handle;
|
||||
struct elfhdr *ehdr;
|
||||
struct elf_shdr *shdr;
|
||||
unsigned long offset, strtbl_off;
|
||||
|
||||
if (!rd_dev->consumer_present) {
|
||||
pr_err("Ramdump(%s): No consumers. Aborting..\n", rd_dev->name);
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
rd_dev->segments = segments;
|
||||
rd_dev->nsegments = nsegments;
|
||||
|
||||
rd_dev->elfcore_size = sizeof(*ehdr) +
|
||||
(sizeof(*shdr) * (nsegments + 2)) + MAX_STRTBL_SIZE;
|
||||
ehdr = kzalloc(rd_dev->elfcore_size, GFP_KERNEL);
|
||||
rd_dev->elfcore_buf = (char *)ehdr;
|
||||
if (!rd_dev->elfcore_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
|
||||
ehdr->e_ident[EI_CLASS] = ELF_CLASS;
|
||||
ehdr->e_ident[EI_DATA] = ELF_DATA;
|
||||
ehdr->e_ident[EI_VERSION] = EV_CURRENT;
|
||||
ehdr->e_ident[EI_OSABI] = ELF_OSABI;
|
||||
ehdr->e_type = ET_CORE;
|
||||
ehdr->e_machine = ELF_ARCH;
|
||||
ehdr->e_version = EV_CURRENT;
|
||||
ehdr->e_ehsize = sizeof(*ehdr);
|
||||
ehdr->e_shoff = sizeof(*ehdr);
|
||||
ehdr->e_shentsize = sizeof(*shdr);
|
||||
ehdr->e_shstrndx = 1;
|
||||
|
||||
|
||||
offset = rd_dev->elfcore_size;
|
||||
shdr = (struct elf_shdr *)(ehdr + 1);
|
||||
strtbl_off = sizeof(*ehdr) + sizeof(*shdr) * (nsegments + 2);
|
||||
shdr++;
|
||||
shdr->sh_type = SHT_STRTAB;
|
||||
shdr->sh_offset = (elf_addr_t)strtbl_off;
|
||||
shdr->sh_size = MAX_STRTBL_SIZE;
|
||||
shdr->sh_entsize = 0;
|
||||
shdr->sh_flags = 0;
|
||||
shdr->sh_name = set_section_name("STR_TBL", ehdr);
|
||||
shdr++;
|
||||
|
||||
for (i = 0; i < nsegments; i++, shdr++) {
|
||||
/* Update elf header */
|
||||
shdr->sh_type = SHT_PROGBITS;
|
||||
shdr->sh_name = set_section_name(segments[i].name, ehdr);
|
||||
shdr->sh_addr = (elf_addr_t)segments[i].address;
|
||||
shdr->sh_size = segments[i].size;
|
||||
shdr->sh_flags = SHF_WRITE;
|
||||
shdr->sh_offset = offset;
|
||||
shdr->sh_entsize = 0;
|
||||
offset += shdr->sh_size;
|
||||
}
|
||||
ehdr->e_shnum = nsegments + 2;
|
||||
|
||||
rd_dev->data_ready = 1;
|
||||
rd_dev->ramdump_status = -1;
|
||||
|
||||
reinit_completion(&rd_dev->ramdump_complete);
|
||||
|
||||
/* Tell userspace that the data is ready */
|
||||
wake_up(&rd_dev->dump_wait_q);
|
||||
|
||||
/* Wait (with a timeout) to let the ramdump complete */
|
||||
ret = wait_for_completion_timeout(&rd_dev->ramdump_complete,
|
||||
msecs_to_jiffies(RAMDUMP_WAIT_MSECS));
|
||||
|
||||
if (!ret) {
|
||||
pr_err("Ramdump(%s): Timed out waiting for userspace.\n",
|
||||
rd_dev->name);
|
||||
ret = -EPIPE;
|
||||
} else {
|
||||
ret = (rd_dev->ramdump_status == 0) ? 0 : -EPIPE;
|
||||
}
|
||||
|
||||
rd_dev->data_ready = 0;
|
||||
rd_dev->elfcore_size = 0;
|
||||
kfree(rd_dev->elfcore_buf);
|
||||
rd_dev->elfcore_buf = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int do_ramdump(void *handle, struct ramdump_segment *segments, int nsegments)
|
||||
{
|
||||
return _do_ramdump(handle, segments, nsegments, false);
|
||||
}
|
||||
EXPORT_SYMBOL(do_ramdump);
|
||||
|
||||
int do_minidump(void *handle, struct ramdump_segment *segments, int nsegments)
|
||||
{
|
||||
return _do_minidump(handle, segments, nsegments);
|
||||
}
|
||||
EXPORT_SYMBOL(do_minidump);
|
||||
|
||||
int
|
||||
do_elf_ramdump(void *handle, struct ramdump_segment *segments, int nsegments)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2011-2014, 2017 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
@ -16,6 +16,7 @@
|
|||
struct device;
|
||||
|
||||
struct ramdump_segment {
|
||||
char *name;
|
||||
unsigned long address;
|
||||
void *v_address;
|
||||
unsigned long size;
|
||||
|
@ -28,6 +29,8 @@ extern int do_ramdump(void *handle, struct ramdump_segment *segments,
|
|||
int nsegments);
|
||||
extern int do_elf_ramdump(void *handle, struct ramdump_segment *segments,
|
||||
int nsegments);
|
||||
extern int do_minidump(void *handle, struct ramdump_segment *segments,
|
||||
int nsegments);
|
||||
|
||||
#else
|
||||
static inline void *create_ramdump_device(const char *dev_name,
|
||||
|
|
Loading…
Add table
Reference in a new issue