Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5d4442b4 authored by Lingutla Chandrasekhar's avatar Lingutla Chandrasekhar
Browse files

soc: qcom: Use ToC design for Minidump support



The Minidump design is re architected to decentralize,
more secure, and to be more scalable.
New design uses table of content for each subsystem to
decentralize, and encryption status to secure regions.
Each subsystem have fixed index in global toc, and
its regions table can be located in its own memory.

Change-Id: I104596e51cf976000b97b2519ce7dcd43e72abb4
Signed-off-by: default avatarLingutla Chandrasekhar <clingutla@codeaurora.org>
parent d56ac6f2
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -260,6 +260,14 @@ config QCOM_MINIDUMP
	  Minidump would dump all registered entries, only when DLOAD mode
	  is enabled.

config MINIDUMP_MAX_ENTRIES
	int "Minidump Maximum num of entries"
	default 200
	depends on QCOM_MINIDUMP
	help
	  This defines maximum number of entries to be allocated for application
	  subsytem in Minidump table.

config QCOM_BUS_SCALING
	bool "Bus scaling driver"
	help
+85 −0
Original line number Diff line number Diff line
/* Copyright (c) 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
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#ifndef __MINIDUMP_PRIVATE_H
#define __MINIDUMP_PRIVATE_H

#define MD_REVISION		1
#define SBL_MINIDUMP_SMEM_ID	602
#define MAX_NUM_OF_SS		10
#define MD_SS_HLOS_ID		0
#define SMEM_ENTRY_SIZE		40

/* Bootloader has 16 byte support, 4 bytes reserved for itself */
#define MAX_REGION_NAME_LENGTH	16

#define MD_REGION_VALID		('V' << 24 | 'A' << 16 | 'L' << 8 | 'I' << 0)
#define MD_REGION_INVALID	('I' << 24 | 'N' << 16 | 'V' << 8 | 'A' << 0)
#define MD_REGION_INIT		('I' << 24 | 'N' << 16 | 'I' << 8 | 'T' << 0)
#define MD_REGION_NOINIT	0

#define MD_SS_ENCR_REQ		(0 << 24 | 'Y' << 16 | 'E' << 8 | 'S' << 0)
#define MD_SS_ENCR_NOTREQ	(0 << 24 | 0 << 16 | 'N' << 8 | 'R' << 0)
#define MD_SS_ENCR_NONE		('N' << 24 | 'O' << 16 | 'N' << 8 | 'E' << 0)
#define MD_SS_ENCR_DONE		('D' << 24 | 'O' << 16 | 'N' << 8 | 'E' << 0)
#define MD_SS_ENCR_START	('S' << 24 | 'T' << 16 | 'R' << 8 | 'T' << 0)
#define MD_SS_ENABLED		('E' << 24 | 'N' << 16 | 'B' << 8 | 'L' << 0)
#define MD_SS_DISABLED		('D' << 24 | 'S' << 16 | 'B' << 8 | 'L' << 0)

/**
 * md_ss_region - Minidump region
 * @name		: Name of the region to be dumped
 * @seq_num:		: Use to differentiate regions with same name.
 * @md_valid		: This entry to be dumped (if set to 1)
 * @region_base_address	: Physical address of region to be dumped
 * @region_size		: Size of the region
 */
struct md_ss_region {
	char	name[MAX_REGION_NAME_LENGTH];
	u32	seq_num;
	u32	md_valid;
	u64	region_base_address;
	u64	region_size;
};

/**
 * md_ss_toc: Sub system SMEM Table of content
 * @md_ss_toc_init : SS toc init status
 * @md_ss_enable_status : if set to 1, Bootloader would dump this SS regions
 * @encryption_status: Encryption status for this subsystem
 * @encryption_required : Decides to encrypt the SS regions or not
 * @ss_region_count : Number of regions added in this SS toc
 * @md_ss_smem_regions_baseptr : regions base pointer of the Subsystem
 */
struct md_ss_toc {
	u32			md_ss_toc_init;
	u32			md_ss_enable_status;
	u32			encryption_status;
	u32			encryption_required;
	u32			ss_region_count;
	struct md_ss_region	*md_ss_smem_regions_baseptr;
};

/**
 * md_global_toc: Global Table of Content
 * @md_toc_init : Global Minidump init status
 * @md_revision : Minidump revision
 * @md_enable_status : Minidump enable status
 * @md_ss_toc : Array of subsystems toc
 */
struct md_global_toc {
	u32			md_toc_init;
	u32			md_revision;
	u32			md_enable_status;
	struct md_ss_toc	md_ss_toc[MAX_NUM_OF_SS];
};

#endif
+140 −123
Original line number Diff line number Diff line
@@ -20,58 +20,39 @@
#include <linux/string.h>
#include <linux/slab.h>
#include <soc/qcom/smem.h>
#include <soc/qcom/scm.h>
#include <soc/qcom/minidump.h>


#define MAX_NUM_ENTRIES		200
#define SMEM_ENTRY_SIZE		32
#define MAX_MEM_LENGTH		(SMEM_ENTRY_SIZE * MAX_NUM_ENTRIES)
#define MAX_STRTBL_SIZE		(MAX_NUM_ENTRIES * MAX_NAME_LENGTH)
#define SMEM_MINIDUMP_TABLE_ID	602

/* Bootloader Minidump table */
struct md_smem_table {
	u32	version;
	u32	smem_length;
	u64	next_avail_offset;
	char	reserved[MAX_NAME_LENGTH];
	u64	*region_start;
};

/* Bootloader Minidump region */
struct md_smem_region {
	char	name[MAX_NAME_LENGTH];
	u64	address;
	u64	size;
};

/* md_table: APPS minidump table
 * @num_regions: Number of entries registered
 * @region_base_offset: APPS region start offset smem table
 * @md_smem_table: Pointer smem table
 * @region: Pointer to APPS region in smem table
 * @entry: All registered client entries.
#include "minidump_private.h"

#define MAX_NUM_ENTRIES         (CONFIG_MINIDUMP_MAX_ENTRIES + 1)
#define MAX_STRTBL_SIZE		(MAX_NUM_ENTRIES * MAX_REGION_NAME_LENGTH)

/**
 * md_table : Local Minidump toc holder
 * @num_regions : Number of regions requested
 * @md_ss_toc  : HLOS toc pointer
 * @md_gbl_toc : Global toc pointer
 * @md_regions : HLOS regions base pointer
 * @entry : array of HLOS regions requested
 */

struct md_table {
	u32			revision;
	u32                     num_regions;
	u32			region_base_offset;
	struct md_smem_table	*md_smem_table;
	struct md_smem_region	*region;
	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];
};

/*
/**
 * md_elfhdr: Minidump table elf header
 * @md_ehdr: elf main header
 * @ehdr: elf main header
 * @shdr: Section header
 * @phdr: Program header
 * @elf_offset: section offset in elf
 * @strtable_idx: string table current index position
 */
struct md_elfhdr {
	struct elfhdr	*md_ehdr;
	struct elfhdr		*ehdr;
	struct elf_shdr		*shdr;
	struct elf_phdr		*phdr;
	u64			elf_offset;
@@ -83,9 +64,8 @@ static DEFINE_SPINLOCK(mdt_lock);
static struct md_table		minidump_table;
static struct md_elfhdr		minidump_elfheader;

bool minidump_enabled;
/* Number of pending entries to be added in ToC regions */
static unsigned int pendings;
static unsigned int region_idx = 1; /* First entry is ELF header*/

static inline char *elf_lookup_string(struct elfhdr *hdr, int offset)
{
@@ -98,7 +78,7 @@ static inline char *elf_lookup_string(struct elfhdr *hdr, int offset)

static inline unsigned int set_section_name(const char *name)
{
	char *strtab = elf_str_table(minidump_elfheader.md_ehdr);
	char *strtab = elf_str_table(minidump_elfheader.ehdr);
	int idx = minidump_elfheader.strtable_idx;
	int ret = 0;

@@ -106,13 +86,12 @@ static inline unsigned int set_section_name(const char *name)
		return 0;

	ret = idx;
	idx += strlcpy((strtab + idx), name, MAX_NAME_LENGTH);
	idx += strlcpy((strtab + idx), name, MAX_REGION_NAME_LENGTH);
	minidump_elfheader.strtable_idx = idx + 1;

	return ret;
}

/* return 1 if name already exists */
static inline bool md_check_name(const char *name)
{
	struct md_region *mde = minidump_table.entry;
@@ -124,25 +103,43 @@ static inline bool md_check_name(const char *name)
	return false;
}

/* Return next seq no, if name already exists in the table */
static inline int md_get_seq_num(const char *name)
{
	struct md_ss_region *mde = minidump_table.md_regions;
	int i, regno = minidump_table.md_ss_toc->ss_region_count;
	int seqno = 0;

	for (i = 0; i < (regno - 1); i++, mde++) {
		if (!strcmp(mde->name, name)) {
			if (mde->seq_num >= seqno)
				seqno = mde->seq_num + 1;
		}
	}
	return seqno;
}

/* Update Mini dump table in SMEM */
static int md_update_smem_table(const struct md_region *entry)
static void md_update_ss_toc(const struct md_region *entry)
{
	struct md_smem_region *mdr;
	struct elfhdr *hdr = minidump_elfheader.md_ehdr;
	struct md_ss_region *mdr;
	struct elfhdr *hdr = minidump_elfheader.ehdr;
	struct elf_shdr *shdr = elf_section(hdr, hdr->e_shnum++);
	struct elf_phdr *phdr = elf_program(hdr, hdr->e_phnum++);
	int reg_cnt = minidump_table.md_ss_toc->ss_region_count++;

	mdr = &minidump_table.region[region_idx++];
	mdr = &minidump_table.md_regions[reg_cnt];

	strlcpy(mdr->name, entry->name, sizeof(mdr->name));
	mdr->address = entry->phys_addr;
	mdr->size = entry->size;
	mdr->region_base_address = entry->phys_addr;
	mdr->region_size = entry->size;
	mdr->seq_num = md_get_seq_num(entry->name);

	/* Update elf header */
	shdr->sh_type = SHT_PROGBITS;
	shdr->sh_name = set_section_name(mdr->name);
	shdr->sh_addr = (elf_addr_t)entry->virt_addr;
	shdr->sh_size = mdr->size;
	shdr->sh_size = mdr->region_size;
	shdr->sh_flags = SHF_WRITE;
	shdr->sh_offset = minidump_elfheader.elf_offset;
	shdr->sh_entsize = 0;
@@ -151,13 +148,26 @@ static int md_update_smem_table(const struct md_region *entry)
	phdr->p_offset = minidump_elfheader.elf_offset;
	phdr->p_vaddr = entry->virt_addr;
	phdr->p_paddr = entry->phys_addr;
	phdr->p_filesz = phdr->p_memsz =  mdr->size;
	phdr->p_filesz = phdr->p_memsz =  mdr->region_size;
	phdr->p_flags = PF_R | PF_W;

	minidump_elfheader.elf_offset += shdr->sh_size;
	mdr->md_valid = MD_REGION_VALID;
}

	return 0;
bool msm_minidump_enabled(void)
{
	bool ret = false;

	spin_lock(&mdt_lock);
	if (minidump_table.md_ss_toc &&
		(minidump_table.md_ss_toc->md_ss_enable_status ==
		 MD_SS_ENABLED))
		ret = true;
	spin_unlock(&mdt_lock);
	return ret;
}
EXPORT_SYMBOL(msm_minidump_enabled);

int msm_minidump_add_region(const struct md_region *entry)
{
@@ -168,8 +178,8 @@ int msm_minidump_add_region(const struct md_region *entry)
	if (!entry)
		return -EINVAL;

	if (((strlen(entry->name) > MAX_NAME_LENGTH) ||
		 md_check_name(entry->name)) && !entry->virt_addr) {
	if ((strlen(entry->name) > MAX_NAME_LENGTH) ||
		md_check_name(entry->name) || !entry->virt_addr) {
		pr_err("Invalid entry details\n");
		return -EINVAL;
	}
@@ -196,64 +206,65 @@ int msm_minidump_add_region(const struct md_region *entry)

	minidump_table.num_regions = entries + 1;

	if (minidump_enabled)
		ret = md_update_smem_table(entry);
	if (minidump_table.md_ss_toc &&
		(minidump_table.md_ss_toc->md_ss_enable_status ==
		MD_SS_ENABLED))
		md_update_ss_toc(entry);
	else
		pendings++;

	spin_unlock(&mdt_lock);

	pr_debug("Minidump: added %s to %s list\n",
			 mdr->name, minidump_enabled ? "":"pending");
	return ret;
}
EXPORT_SYMBOL(msm_minidump_add_region);

static int msm_minidump_add_header(void)
{
	struct md_smem_region *mdreg = &minidump_table.region[0];
	struct elfhdr *md_ehdr;
	struct md_ss_region *mdreg = &minidump_table.md_regions[0];
	struct elfhdr *ehdr;
	struct elf_shdr *shdr;
	struct elf_phdr *phdr;
	unsigned int strtbl_off, elfh_size, phdr_off;
	char *banner;

	/* Header buffer contains:
	 * elf header, MAX_NUM_ENTRIES+1 of section and program elf headers,
	 * elf header, MAX_NUM_ENTRIES+4 of section and program elf headers,
	 * string table section and linux banner.
	 */
	elfh_size = sizeof(*md_ehdr) + MAX_STRTBL_SIZE + MAX_MEM_LENGTH +
		((sizeof(*shdr) + sizeof(*phdr)) * (MAX_NUM_ENTRIES + 1));
	elfh_size = sizeof(*ehdr) + MAX_STRTBL_SIZE + (strlen(linux_banner) +
		1) + ((sizeof(*shdr) + sizeof(*phdr)) * (MAX_NUM_ENTRIES + 4));
	elfh_size = ALIGN(elfh_size, 4);

	minidump_elfheader.md_ehdr = kzalloc(elfh_size, GFP_KERNEL);
	if (!minidump_elfheader.md_ehdr)
	minidump_elfheader.ehdr = kzalloc(elfh_size, GFP_KERNEL);
	if (!minidump_elfheader.ehdr)
		return -ENOMEM;

	strlcpy(mdreg->name, "KELF_HEADER", sizeof(mdreg->name));
	mdreg->address = virt_to_phys(minidump_elfheader.md_ehdr);
	mdreg->size = elfh_size;
	mdreg->region_base_address = virt_to_phys(minidump_elfheader.ehdr);
	mdreg->region_size = elfh_size;

	md_ehdr = minidump_elfheader.md_ehdr;
	ehdr = minidump_elfheader.ehdr;
	/* Assign section/program headers offset */
	minidump_elfheader.shdr = shdr = (struct elf_shdr *)(md_ehdr + 1);
	minidump_elfheader.shdr = shdr = (struct elf_shdr *)(ehdr + 1);
	minidump_elfheader.phdr = phdr =
				 (struct elf_phdr *)(shdr + MAX_NUM_ENTRIES);
	phdr_off = sizeof(*md_ehdr) + (sizeof(*shdr) * MAX_NUM_ENTRIES);

	memcpy(md_ehdr->e_ident, ELFMAG, SELFMAG);
	md_ehdr->e_ident[EI_CLASS] = ELF_CLASS;
	md_ehdr->e_ident[EI_DATA] = ELF_DATA;
	md_ehdr->e_ident[EI_VERSION] = EV_CURRENT;
	md_ehdr->e_ident[EI_OSABI] = ELF_OSABI;
	md_ehdr->e_type = ET_CORE;
	md_ehdr->e_machine  = ELF_ARCH;
	md_ehdr->e_version = EV_CURRENT;
	md_ehdr->e_ehsize = sizeof(*md_ehdr);
	md_ehdr->e_phoff = phdr_off;
	md_ehdr->e_phentsize = sizeof(*phdr);
	md_ehdr->e_shoff = sizeof(*md_ehdr);
	md_ehdr->e_shentsize = sizeof(*shdr);
	md_ehdr->e_shstrndx = 1;
	phdr_off = sizeof(*ehdr) + (sizeof(*shdr) * MAX_NUM_ENTRIES);

	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_phoff = phdr_off;
	ehdr->e_phentsize = sizeof(*phdr);
	ehdr->e_shoff = sizeof(*ehdr);
	ehdr->e_shentsize = sizeof(*shdr);
	ehdr->e_shstrndx = 1;

	minidump_elfheader.elf_offset = elfh_size;

@@ -262,7 +273,7 @@ static int msm_minidump_add_header(void)
	 * 2nd section is string table.
	 */
	minidump_elfheader.strtable_idx = 1;
	strtbl_off = sizeof(*md_ehdr) +
	strtbl_off = sizeof(*ehdr) +
			((sizeof(*phdr) + sizeof(*shdr)) * MAX_NUM_ENTRIES);
	shdr++;
	shdr->sh_type = SHT_STRTAB;
@@ -282,8 +293,8 @@ static int msm_minidump_add_header(void)
	shdr++;

	/* 4th section is linux banner */
	banner = (char *)md_ehdr + strtbl_off + MAX_STRTBL_SIZE;
	strlcpy(banner, linux_banner, MAX_MEM_LENGTH);
	banner = (char *)ehdr + strtbl_off + MAX_STRTBL_SIZE;
	strlcpy(banner, linux_banner, strlen(linux_banner) + 1);

	shdr->sh_type = SHT_PROGBITS;
	shdr->sh_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE);
@@ -301,9 +312,10 @@ static int msm_minidump_add_header(void)
	phdr->p_flags = PF_R | PF_W;

	/* Update headers count*/
	md_ehdr->e_phnum = 1;
	md_ehdr->e_shnum = 4;
	ehdr->e_phnum = 1;
	ehdr->e_shnum = 4;

	mdreg->md_valid = MD_REGION_VALID;
	return 0;
}

@@ -311,52 +323,57 @@ static int __init msm_minidump_init(void)
{
	unsigned int i, size;
	struct md_region *mdr;
	struct md_smem_table *smem_table;
	struct md_global_toc *md_global_toc;
	struct md_ss_toc *md_ss_toc;

	/* Get Minidump table */
	smem_table = smem_get_entry(SMEM_MINIDUMP_TABLE_ID, &size, 0,
	md_global_toc = smem_get_entry(SBL_MINIDUMP_SMEM_ID, &size, 0,
					SMEM_ANY_HOST_FLAG);
	if (IS_ERR_OR_NULL(smem_table)) {
	if (IS_ERR_OR_NULL(md_global_toc)) {
		pr_err("SMEM is not initialized.\n");
		return -ENODEV;
	}

	if ((smem_table->next_avail_offset + MAX_MEM_LENGTH) >
		 smem_table->smem_length) {
		pr_err("SMEM memory not available.\n");
		return -ENOMEM;
	/*Check global minidump support initialization */
	if (!md_global_toc->md_toc_init) {
		pr_err("System Minidump TOC not initialized\n");
		return -ENODEV;
	}

	/* Get next_avail_offset and update it to reserve memory */
	minidump_table.region_base_offset = smem_table->next_avail_offset;
	minidump_table.region = (struct md_smem_region *)((uintptr_t)smem_table
				+ minidump_table.region_base_offset);
	minidump_table.md_gbl_toc = md_global_toc;
	minidump_table.revision = md_global_toc->md_revision;
	md_ss_toc = &md_global_toc->md_ss_toc[MD_SS_HLOS_ID];

	md_ss_toc->encryption_status = MD_SS_ENCR_NONE;
	md_ss_toc->encryption_required = MD_SS_ENCR_REQ;

	minidump_table.md_ss_toc = md_ss_toc;
	minidump_table.md_regions = kzalloc((MAX_NUM_ENTRIES *
				sizeof(struct md_ss_region)), GFP_KERNEL);
	if (!minidump_table.md_regions)
		return -ENOMEM;

	smem_table->next_avail_offset =
			minidump_table.region_base_offset + MAX_MEM_LENGTH;
	minidump_table.md_smem_table = smem_table;
	md_ss_toc->md_ss_smem_regions_baseptr =
				(void *)virt_to_phys(minidump_table.md_regions);

	/* First entry would be ELF header */
	md_ss_toc->ss_region_count = 1;
	msm_minidump_add_header();

	/* Add pending entries to smem table */
	/* Add pending entries to HLOS TOC */
	spin_lock(&mdt_lock);
	minidump_enabled = true;

	md_ss_toc->md_ss_toc_init = 1;
	md_ss_toc->md_ss_enable_status = MD_SS_ENABLED;
	for (i = 0; i < pendings; i++) {
		mdr = &minidump_table.entry[i];
		if (md_update_smem_table(mdr)) {
			pr_err("Unable to add entry %s to smem table\n",
				mdr->name);
			spin_unlock(&mdt_lock);
			return -ENOENT;
		}
		md_update_ss_toc(mdr);
	}

	pendings = 0;
	spin_unlock(&mdt_lock);

	pr_info("Enabled, region base:%d, region 0x%pK\n",
		 minidump_table.region_base_offset, minidump_table.region);
	pr_info("Enabled with max number of regions %d\n",
		CONFIG_MINIDUMP_MAX_ENTRIES);

	return 0;
}
+3 −6
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@
#ifndef __MINIDUMP_H
#define __MINIDUMP_H

#define MAX_NAME_LENGTH		16
#define MAX_NAME_LENGTH		12
/* md_region -  Minidump table entry
 * @name:	Entry name, Minidump will dump binary with this name.
 * @id:		Entry ID, used only for SDI dumps.
@@ -37,8 +37,7 @@ struct md_region {
 */
#ifdef CONFIG_QCOM_MINIDUMP
extern int msm_minidump_add_region(const struct md_region *entry);
/* Sets to true, if minidump table is initialized */
extern bool minidump_enabled;
extern bool msm_minidump_enabled(void);
extern void dump_stack_minidump(u64 sp);
#else
static inline int msm_minidump_add_region(const struct md_region *entry)
@@ -46,9 +45,7 @@ static inline int msm_minidump_add_region(const struct md_region *entry)
	/* Return quietly, if minidump is not supported */
	return 0;
}

static inline bool msm_minidump_enabled(void) { return false; }
static inline void dump_stack_minidump(u64 sp) {}
#endif


#endif