410 lines
12 KiB
C
410 lines
12 KiB
C
/*
|
|
* sec_debug_summary.c
|
|
*
|
|
* driver supporting debug functions for Samsung device
|
|
*
|
|
* COPYRIGHT(C) Samsung Electronics Co., Ltd. 2006-2015 All Right Reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/ctype.h>
|
|
#include <linux/qcom/sec_debug.h>
|
|
#include <linux/qcom/sec_debug_summary.h>
|
|
#include <soc/qcom/smem.h>
|
|
#include <linux/ptrace.h>
|
|
#include <asm/processor.h>
|
|
|
|
struct sec_debug_summary *secdbg_summary;
|
|
struct sec_debug_summary_data_apss *secdbg_apss;
|
|
extern unsigned int secdbg_paddr;
|
|
extern unsigned int secdbg_size;
|
|
extern struct sec_debug_log *secdbg_log;
|
|
static char build_root[] = __FILE__;
|
|
|
|
extern unsigned int system_rev;
|
|
static uint32_t tzapps_start_addr;
|
|
static uint32_t tzapps_size;
|
|
extern int sec_debug_summary_set_msm_dump_info(struct sec_debug_summary_data_apss *apss);
|
|
|
|
char *sec_debug_arch_desc;
|
|
#ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML
|
|
unsigned int cpu_frequency[CONFIG_NR_CPUS];
|
|
unsigned int cpu_volt[CONFIG_NR_CPUS];
|
|
char cpu_state[CONFIG_NR_CPUS][VAR_NAME_MAX];
|
|
EXPORT_SYMBOL(cpu_frequency);
|
|
EXPORT_SYMBOL(cpu_volt);
|
|
EXPORT_SYMBOL(cpu_state);
|
|
#endif
|
|
|
|
#ifdef CONFIG_ARM64
|
|
#define ARM_PT_REG_PC pc
|
|
#define ARM_PT_REG_LR regs[30]
|
|
#else
|
|
#define ARM_PT_REG_PC ARM_pc
|
|
#define ARM_PT_REG_LR ARM_lr
|
|
#endif
|
|
|
|
int sec_debug_save_die_info(const char *str, struct pt_regs *regs)
|
|
{
|
|
if (!secdbg_apss)
|
|
return -ENOMEM;
|
|
snprintf(secdbg_apss->excp.pc_sym, sizeof(secdbg_apss->excp.pc_sym),
|
|
"%pS", (void *)regs->ARM_PT_REG_PC);
|
|
snprintf(secdbg_apss->excp.lr_sym, sizeof(secdbg_apss->excp.lr_sym),
|
|
"%pS", (void *)regs->ARM_PT_REG_LR);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sec_debug_save_panic_info(const char *str, unsigned long caller)
|
|
{
|
|
if (!secdbg_apss)
|
|
return -ENOMEM;
|
|
snprintf(secdbg_apss->excp.panic_caller,
|
|
sizeof(secdbg_apss->excp.panic_caller), "%pS", (void *)caller);
|
|
snprintf(secdbg_apss->excp.panic_msg,
|
|
sizeof(secdbg_apss->excp.panic_msg), "%s", str);
|
|
snprintf(secdbg_apss->excp.thread,
|
|
sizeof(secdbg_apss->excp.thread), "%s:%d", current->comm,
|
|
task_pid_nr(current));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sec_debug_summary_add_infomon(char *name, unsigned int size, uint64_t pa)
|
|
{
|
|
if (!secdbg_apss)
|
|
return -ENOMEM;
|
|
|
|
if (secdbg_apss->info_mon.idx >= ARRAY_SIZE(secdbg_apss->info_mon.var))
|
|
return -ENOMEM;
|
|
|
|
strlcpy(secdbg_apss->info_mon.var[secdbg_apss->info_mon.idx].name,
|
|
name, sizeof(secdbg_apss->info_mon.var[0].name));
|
|
secdbg_apss->info_mon.var[secdbg_apss->info_mon.idx].sizeof_type
|
|
= size;
|
|
secdbg_apss->info_mon.var[secdbg_apss->info_mon.idx].var_paddr = pa;
|
|
|
|
secdbg_apss->info_mon.idx++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sec_debug_summary_add_varmon(char *name, unsigned int size, uint64_t pa)
|
|
{
|
|
if (!secdbg_apss)
|
|
return -ENOMEM;
|
|
|
|
if (secdbg_apss->var_mon.idx > ARRAY_SIZE(secdbg_apss->var_mon.var))
|
|
return -ENOMEM;
|
|
|
|
strlcpy(secdbg_apss->var_mon.var[secdbg_apss->var_mon.idx].name, name,
|
|
sizeof(secdbg_apss->var_mon.var[0].name));
|
|
secdbg_apss->var_mon.var[secdbg_apss->var_mon.idx].sizeof_type = size;
|
|
secdbg_apss->var_mon.var[secdbg_apss->var_mon.idx].var_paddr = pa;
|
|
|
|
secdbg_apss->var_mon.idx++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_SEC_DEBUG_MDM_FILE_INFO
|
|
void sec_set_mdm_summary_info(char *str_buf)
|
|
{
|
|
snprintf(secdbg_apss->mdmerr_info,
|
|
sizeof(secdbg_apss->mdmerr_info), "%s", str_buf);
|
|
}
|
|
#endif
|
|
|
|
static int ___build_root_init(char *str)
|
|
{
|
|
char *st, *ed;
|
|
int len;
|
|
ed = strstr(str, "/android/kernel");
|
|
if (!ed || ed == str)
|
|
return -1;
|
|
*ed = '\0';
|
|
st = strrchr(str, '/');
|
|
if (!st)
|
|
return -1;
|
|
st++;
|
|
len = (unsigned long)ed - (unsigned long)st + 1;
|
|
memmove(str, st, len);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML
|
|
void sec_debug_save_cpu_freq_voltage(int cpu, int flag, unsigned long value)
|
|
{
|
|
if (SAVE_FREQ == flag)
|
|
cpu_frequency[cpu] = value;
|
|
else if (SAVE_VOLT == flag)
|
|
cpu_volt[cpu] = (unsigned int)value;
|
|
}
|
|
#else
|
|
void sec_debug_save_cpu_freq_voltage(int cpu, int flag, unsigned long value)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
void sec_debug_secure_app_addr_size(uint32_t addr, uint32_t size)
|
|
{
|
|
tzapps_start_addr = addr;
|
|
tzapps_size = size;
|
|
}
|
|
|
|
static int __init _set_kconst(struct sec_debug_summary_data_apss *p)
|
|
{
|
|
p->kconst.nr_cpus = NR_CPUS;
|
|
p->kconst.per_cpu_offset.pa = virt_to_phys(__per_cpu_offset);
|
|
p->kconst.per_cpu_offset.size = sizeof(__per_cpu_offset[0]);
|
|
p->kconst.per_cpu_offset.count = sizeof(__per_cpu_offset)/sizeof(__per_cpu_offset[0]);
|
|
p->kconst.virt_to_phys = (uint64_t)virt_to_phys(0);
|
|
p->kconst.phys_to_virt = (uint64_t)phys_to_virt(0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __init summary_init_infomon(void)
|
|
{
|
|
if (___build_root_init(build_root) == 0)
|
|
ADD_STR_TO_INFOMON(build_root);
|
|
ADD_STR_TO_INFOMON(linux_banner);
|
|
sec_debug_summary_add_infomon("Kernel cmdline", -1, (uint64_t)__pa(saved_command_line));
|
|
sec_debug_summary_add_infomon("Hardware name", -1, (uint64_t)__pa(sec_debug_arch_desc));
|
|
ADD_VAR_TO_INFOMON(system_rev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __init summary_init_varmon(void)
|
|
{
|
|
uint64_t last_pet_paddr = 0;
|
|
uint64_t last_ns_paddr = 0;
|
|
|
|
/* save paddrs of last_pet und last_ns */
|
|
if (secdbg_paddr && secdbg_log) {
|
|
last_pet_paddr = secdbg_paddr +
|
|
offsetof(struct sec_debug_log, last_pet);
|
|
last_ns_paddr = secdbg_paddr +
|
|
offsetof(struct sec_debug_log, last_ns);
|
|
sec_debug_summary_add_varmon("last_pet",
|
|
sizeof((secdbg_log->last_pet)), last_pet_paddr);
|
|
sec_debug_summary_add_varmon("last_ns",
|
|
sizeof((secdbg_log->last_ns.counter)),
|
|
last_ns_paddr);
|
|
} else
|
|
pr_emerg("**** secdbg_log or secdbg_paddr is not initialized ****\n");
|
|
|
|
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
|
|
ADD_VAR_TO_VARMON(boot_reason);
|
|
ADD_VAR_TO_VARMON(cold_boot);
|
|
#endif
|
|
|
|
#ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML
|
|
for(i = 0; i < CONFIG_NR_CPUS; i++) {
|
|
ADD_STR_ARRAY_TO_VARMON(cpu_state[i], i, CPU_STAT_CORE);
|
|
ADD_ARRAY_TO_VARMON(cpu_frequency[i], i, CPU_FREQ_CORE);
|
|
ADD_ARRAY_TO_VARMON(cpu_volt[i], i, CPU_VOLT_CORE);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __init summary_init_sched_log(struct sec_debug_summary_data_apss *p)
|
|
{
|
|
if (!secdbg_paddr)
|
|
return -ENOMEM;
|
|
|
|
p->sched_log.sched_idx_paddr = secdbg_paddr +
|
|
offsetof(struct sec_debug_log, idx_sched);
|
|
p->sched_log.sched_buf_paddr = secdbg_paddr +
|
|
offsetof(struct sec_debug_log, sched);
|
|
p->sched_log.sched_struct_sz =
|
|
sizeof(struct sched_log);
|
|
p->sched_log.sched_array_cnt = SCHED_LOG_MAX;
|
|
|
|
p->sched_log.irq_idx_paddr = secdbg_paddr +
|
|
offsetof(struct sec_debug_log, idx_irq);
|
|
p->sched_log.irq_buf_paddr = secdbg_paddr +
|
|
offsetof(struct sec_debug_log, irq);
|
|
p->sched_log.irq_struct_sz =
|
|
sizeof(struct irq_log);
|
|
p->sched_log.irq_array_cnt = SCHED_LOG_MAX;
|
|
|
|
p->sched_log.secure_idx_paddr = secdbg_paddr +
|
|
offsetof(struct sec_debug_log, idx_secure);
|
|
p->sched_log.secure_buf_paddr = secdbg_paddr +
|
|
offsetof(struct sec_debug_log, secure);
|
|
p->sched_log.secure_struct_sz =
|
|
sizeof(struct secure_log);
|
|
p->sched_log.secure_array_cnt = TZ_LOG_MAX;
|
|
|
|
p->sched_log.irq_exit_idx_paddr = secdbg_paddr +
|
|
offsetof(struct sec_debug_log, idx_irq_exit);
|
|
p->sched_log.irq_exit_buf_paddr = secdbg_paddr +
|
|
offsetof(struct sec_debug_log, irq_exit);
|
|
p->sched_log.irq_exit_struct_sz =
|
|
sizeof(struct irq_exit_log);
|
|
p->sched_log.irq_exit_array_cnt = SCHED_LOG_MAX;
|
|
|
|
#ifdef CONFIG_SEC_DEBUG_MSG_LOG
|
|
p->sched_log.msglog_idx_paddr = secdbg_paddr +
|
|
offsetof(struct sec_debug_log, idx_secmsg);
|
|
p->sched_log.msglog_buf_paddr = secdbg_paddr +
|
|
offsetof(struct sec_debug_log, secmsg);
|
|
p->sched_log.msglog_struct_sz =
|
|
sizeof(struct secmsg_log);
|
|
p->sched_log.msglog_array_cnt = MSG_LOG_MAX;
|
|
#else
|
|
p->sched_log.msglog_idx_paddr = 0;
|
|
p->sched_log.msglog_buf_paddr = 0;
|
|
p->sched_log.msglog_struct_sz = 0;
|
|
p->sched_log.msglog_array_cnt = 0;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __init summary_init_core_reg(struct sec_debug_summary_data_apss *p)
|
|
{
|
|
/* setup sec debug core reg info */
|
|
p->cpu_reg.sec_debug_core_reg_paddr = virt_to_phys(&sec_debug_core_reg);
|
|
|
|
pr_info("sec_debug_core_reg_paddr = 0x%llx\n", p->cpu_reg.sec_debug_core_reg_paddr);
|
|
|
|
#ifdef CONFIG_MSM_MEMORY_DUMP_V2
|
|
/* setup qc core reg info */
|
|
sec_debug_summary_set_msm_dump_info(p);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __init summary_init_avc_log(struct sec_debug_summary_data_apss *p)
|
|
{
|
|
if (!secdbg_paddr)
|
|
return -EINVAL;
|
|
|
|
#ifdef CONFIG_SEC_DEBUG_AVC_LOG
|
|
p->avc_log.secavc_idx_paddr = secdbg_paddr +
|
|
offsetof(struct sec_debug_log, idx_secavc);
|
|
p->avc_log.secavc_buf_paddr = secdbg_paddr +
|
|
offsetof(struct sec_debug_log, secavc);
|
|
p->avc_log.secavc_struct_sz =
|
|
sizeof(struct secavc_log);
|
|
p->avc_log.secavc_array_cnt = AVC_LOG_MAX;
|
|
#else
|
|
p->avc_log.secavc_idx_paddr = 0;
|
|
p->avc_log.secavc_buf_paddr = 0;
|
|
p->avc_log.secavc_struct_sz = 0;
|
|
p->avc_log.secavc_array_cnt = 0;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sec_debug_is_modem_seperate_debug_ssr(void)
|
|
{
|
|
return secdbg_summary->priv.modem.seperate_debug;
|
|
}
|
|
|
|
int __init sec_debug_summary_init(void)
|
|
{
|
|
#ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML
|
|
short i;
|
|
#endif
|
|
|
|
pr_info("%s: SMEM_ID_VENDOR0=0x%x size=0x%lx\n",
|
|
__func__, (unsigned int)SMEM_ID_VENDOR2,
|
|
sizeof(struct sec_debug_summary));
|
|
|
|
/* set summary address in smem for other subsystems to see */
|
|
secdbg_summary = (struct sec_debug_summary *)smem_alloc(
|
|
SMEM_ID_VENDOR2,
|
|
sizeof(struct sec_debug_summary),
|
|
0,
|
|
SMEM_ANY_HOST_FLAG);
|
|
|
|
if (secdbg_summary == NULL) {
|
|
pr_info("%s: smem alloc failed!\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memset(secdbg_summary, 0, (unsigned long)sizeof(secdbg_summary));
|
|
|
|
secdbg_summary->secure_app_start_addr = tzapps_start_addr;
|
|
secdbg_summary->secure_app_size = tzapps_size;
|
|
|
|
secdbg_apss = &secdbg_summary->priv.apss;
|
|
|
|
secdbg_summary->apss = (struct sec_debug_summary_data_apss *)
|
|
(smem_virt_to_phys(&secdbg_summary->priv.apss)&0xFFFFFFFF);
|
|
secdbg_summary->rpm = (struct sec_debug_summary_data *)
|
|
(smem_virt_to_phys(&secdbg_summary->priv.rpm)&0xFFFFFFFF);
|
|
secdbg_summary->modem = (struct sec_debug_summary_data_modem *)
|
|
(smem_virt_to_phys(&secdbg_summary->priv.modem)&0xFFFFFFFF);
|
|
secdbg_summary->dsps = (struct sec_debug_summary_data *)
|
|
(smem_virt_to_phys(&secdbg_summary->priv.dsps)&0xFFFFFFFF);
|
|
|
|
pr_info("%s: apss(%lx) rpm(%lx) modem(%lx) dsps(%lx)\n", __func__,
|
|
(unsigned long)secdbg_summary->apss,
|
|
(unsigned long)secdbg_summary->rpm,
|
|
(unsigned long)secdbg_summary->modem,
|
|
(unsigned long)secdbg_summary->dsps);
|
|
|
|
|
|
strlcpy(secdbg_apss->name, "APSS", sizeof(secdbg_apss->name) + 1);
|
|
strlcpy(secdbg_apss->state, "Init", sizeof(secdbg_apss->state) + 1);
|
|
secdbg_apss->nr_cpus = CONFIG_NR_CPUS;
|
|
|
|
sec_debug_summary_set_kloginfo(&secdbg_apss->log.first_idx_paddr,
|
|
&secdbg_apss->log.next_idx_paddr,
|
|
&secdbg_apss->log.log_paddr, &secdbg_apss->log.size);
|
|
sec_debug_summary_set_logger_info(&secdbg_apss->logger_log);
|
|
secdbg_apss->tz_core_dump =
|
|
(struct msm_dump_data **)get_wdog_regsave_paddr();
|
|
|
|
summary_init_infomon();
|
|
|
|
summary_init_varmon();
|
|
|
|
summary_init_sched_log(secdbg_apss);
|
|
|
|
summary_init_core_reg(secdbg_apss);
|
|
|
|
summary_init_avc_log(secdbg_apss);
|
|
|
|
sec_debug_summary_set_kallsyms_info(secdbg_apss);
|
|
|
|
_set_kconst(secdbg_apss);
|
|
sec_debug_summary_set_rtb_info(secdbg_apss);
|
|
|
|
/* fill magic nubmer last to ensure data integrity when the magic
|
|
* numbers are written
|
|
*/
|
|
secdbg_summary->magic[0] = SEC_DEBUG_SUMMARY_MAGIC0;
|
|
secdbg_summary->magic[1] = SEC_DEBUG_SUMMARY_MAGIC1;
|
|
secdbg_summary->magic[2] = SEC_DEBUG_SUMMARY_MAGIC2;
|
|
secdbg_summary->magic[3] = SEC_DEBUG_SUMMARY_MAGIC3;
|
|
|
|
return 0;
|
|
}
|
|
|
|
subsys_initcall_sync(sec_debug_summary_init);
|