Merge "soc: qcom: Add SSR minidump provision for subsystem ramdump"

This commit is contained in:
Linux Build Service Account 2017-08-30 06:26:16 -07:00 committed by Gerrit - the friendly Code Review server
commit 7392248e79
7 changed files with 291 additions and 4 deletions

View file

@ -73,6 +73,11 @@ USB Diag Cookies:
Memory region used to store USB PID and serial numbers to be used by Memory region used to store USB PID and serial numbers to be used by
bootloader in download mode. bootloader in download mode.
SSR Minidump Offset
-------------------
-Compatible: "qcom,msm-imem-minidump"
-reg: start address and size of ssr imem region
Required properties: Required properties:
-compatible: "qcom,msm-imem-diag-dload" -compatible: "qcom,msm-imem-diag-dload"
-reg: start address and size of USB Diag download mode region in imem -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"; compatible = "qcom,msm-imem-emergency_download_mode";
reg = <0xfe0 12>; reg = <0xfe0 12>;
}; };
ss_mdump@b88 {
compatible = "qcom,msm-imem-minidump";
reg = <0xb88 28>;
};
}; };

View file

@ -88,6 +88,7 @@ Optional properties:
- qcom,override-acc-1: Override the default ACC settings with this value if present. - 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 - qcom,cx-ipeak-vote: Boolean- Present if we need to set bit 5 of cxip_lm_vote_clear
during modem shutdown 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 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. needs to be loaded in a specifically carved out memory region.

View file

@ -56,7 +56,9 @@
#endif #endif
#define PIL_NUM_DESC 10 #define PIL_NUM_DESC 10
#define NUM_OF_ENCRYPTED_KEY 3
static void __iomem *pil_info_base; static void __iomem *pil_info_base;
static void __iomem *pil_minidump_base;
/** /**
* proxy_timeout - Override for proxy vote timeouts * proxy_timeout - Override for proxy vote timeouts
@ -79,6 +81,18 @@ struct pil_mdt {
struct elf32_phdr phdr[]; 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 * struct pil_seg - memory map representing one segment
* @next: points to next seg mentor NULL if last segment * @next: points to next seg mentor NULL if last segment
@ -133,11 +147,67 @@ struct pil_priv {
phys_addr_t region_end; phys_addr_t region_end;
void *region; void *region;
struct pil_image_info __iomem *info; struct pil_image_info __iomem *info;
struct md_ssr_ss_info __iomem *minidump;
int minidump_id;
int id; int id;
int unvoted_flag; int unvoted_flag;
size_t region_size; 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, &region_info, sizeof(region_info));
memcpy(&s->name, &region_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 * pil_do_ramdump() - Ramdump an image
* @desc: descriptor from pil_desc_init() * @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; int count = 0, ret;
struct ramdump_segment *ramdump_segs, *s; 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) list_for_each_entry(seg, &priv->segs, list)
count++; count++;
@ -1014,9 +1087,10 @@ bool is_timeout_disabled(void)
int pil_desc_init(struct pil_desc *desc) int pil_desc_init(struct pil_desc *desc)
{ {
struct pil_priv *priv; struct pil_priv *priv;
int ret;
void __iomem *addr; void __iomem *addr;
int ret, ss_imem_offset_mdump;
char buf[sizeof(priv->info->name)]; char buf[sizeof(priv->info->name)];
struct device_node *ofnode = desc->dev->of_node;
if (WARN(desc->ops->proxy_unvote && !desc->ops->proxy_vote, if (WARN(desc->ops->proxy_unvote && !desc->ops->proxy_vote,
"Invalid proxy voting. Ignoring\n")) "Invalid proxy voting. Ignoring\n"))
@ -1039,6 +1113,22 @@ int pil_desc_init(struct pil_desc *desc)
strncpy(buf, desc->name, sizeof(buf)); strncpy(buf, desc->name, sizeof(buf));
__iowrite32_copy(priv->info->name, buf, sizeof(buf) / 4); __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); ret = pil_parse_devicetree(desc);
if (ret) if (ret)
@ -1148,6 +1238,20 @@ static int __init msm_pil_init(void)
for (i = 0; i < resource_size(&res)/sizeof(u32); i++) for (i = 0; i < resource_size(&res)/sizeof(u32); i++)
writel_relaxed(0, pil_info_base + (i * sizeof(u32))); 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: out:
return register_pm_notifier(&pil_pm_notifier); return register_pm_notifier(&pil_pm_notifier);
} }
@ -1158,6 +1262,8 @@ static void __exit msm_pil_exit(void)
unregister_pm_notifier(&pil_pm_notifier); unregister_pm_notifier(&pil_pm_notifier);
if (pil_info_base) if (pil_info_base)
iounmap(pil_info_base); iounmap(pil_info_base);
if (pil_minidump_base)
iounmap(pil_minidump_base);
} }
module_exit(msm_pil_exit); module_exit(msm_pil_exit);

View file

@ -74,6 +74,34 @@ struct pil_image_info {
__le32 size; __le32 size;
} __attribute__((__packed__)); } __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 * struct pil_reset_ops - PIL operations
* @init_image: prepare an image for authentication * @init_image: prepare an image for authentication

View file

@ -78,7 +78,8 @@
#define MSS_MAGIC 0XAABADEAD #define MSS_MAGIC 0XAABADEAD
/* CX_IPEAK Parameters */ /* CX_IPEAK Parameters */
#define CX_IPEAK_MSS BIT(5) #define CX_IPEAK_MSS BIT(5)
/* Timeout value for MBA boot when minidump is enabled */
#define MBA_ENCRYPTION_TIMEOUT 3000
enum scm_cmd { enum scm_cmd {
PAS_MEM_SETUP_CMD = 2, 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; struct device *dev = drv->desc.dev;
int ret; int ret;
u32 status; 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. */ /* Wait for PBL completion. */
ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status, ret = readl_poll_timeout(drv->rmb_base + RMB_PBL_STATUS, status,

View file

@ -29,6 +29,8 @@
#include <linux/of.h> #include <linux/of.h>
#define RAMDUMP_WAIT_MSECS 120000 #define RAMDUMP_WAIT_MSECS 120000
#define MAX_STRTBL_SIZE 512
#define MAX_NAME_LENGTH 16
struct ramdump_device { struct ramdump_device {
char name[256]; 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) int do_ramdump(void *handle, struct ramdump_segment *segments, int nsegments)
{ {
return _do_ramdump(handle, segments, nsegments, false); return _do_ramdump(handle, segments, nsegments, false);
} }
EXPORT_SYMBOL(do_ramdump); 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 int
do_elf_ramdump(void *handle, struct ramdump_segment *segments, int nsegments) do_elf_ramdump(void *handle, struct ramdump_segment *segments, int nsegments)
{ {

View file

@ -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 * 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 * it under the terms of the GNU General Public License version 2 and
@ -16,6 +16,7 @@
struct device; struct device;
struct ramdump_segment { struct ramdump_segment {
char *name;
unsigned long address; unsigned long address;
void *v_address; void *v_address;
unsigned long size; unsigned long size;
@ -28,6 +29,8 @@ extern int do_ramdump(void *handle, struct ramdump_segment *segments,
int nsegments); int nsegments);
extern int do_elf_ramdump(void *handle, struct ramdump_segment *segments, extern int do_elf_ramdump(void *handle, struct ramdump_segment *segments,
int nsegments); int nsegments);
extern int do_minidump(void *handle, struct ramdump_segment *segments,
int nsegments);
#else #else
static inline void *create_ramdump_device(const char *dev_name, static inline void *create_ramdump_device(const char *dev_name,