You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
kernel_samsung_sm7125/drivers/samsung/debug/sec_debug_summary.c

848 lines
25 KiB

// SPDX-License-Identifier: GPL-2.0
/*
* drivers/samsung/debug/sec_debug_summary.c
*
* COPYRIGHT(C) 2006-2019 Samsung Electronics Co., Ltd. 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 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s() " fmt, __func__
#include <linux/kernel.h>
#include <linux/of_fdt.h>
#include <linux/version.h>
#include <linux/platform_device.h>
#include <asm/stacktrace.h>
#include <asm/system_misc.h>
#include <linux/sec_debug.h>
#if defined(CONFIG_MSM_SMEM)
#include <soc/qcom/smem.h>
#else
#include <linux/soc/qcom/smem.h>
#endif
#include "sec_debug_internal.h"
#include "sec_debug_summary_extern.h"
static struct sec_debug_summary *secdbg_summary;
static struct sec_debug_summary_data_apss *secdbg_apss;
static char __sec_debug_arch_desc[128];
static char *sec_debug_arch_desc = __sec_debug_arch_desc;
static void __init summary_init_debug_arch_desc(void)
{
const char *machine_name;
machine_name = of_flat_dt_get_machine_name();
if (!machine_name) {
strlcpy(__sec_debug_arch_desc,
"UNKNOWN SAMSUNG Device - machine_name is not detected",
sizeof(__sec_debug_arch_desc));
return;
}
snprintf(__sec_debug_arch_desc, sizeof(__sec_debug_arch_desc),
"%s (DT)", machine_name);
}
#ifdef CONFIG_SEC_USER_RESET_DEBUG
static void __summary_save_dying_msg_for_user_reset_debug(const char *str,
const void *pc, const void *lr)
{
_kern_ex_info_t *p_ex_info;
sec_debug_store_extc_idx(false);
if (!sec_debug_reset_ex_info)
return;
p_ex_info = &sec_debug_reset_ex_info->kern_ex_info.info;
if (p_ex_info->cpu == -1) {
int slen;
char *msg;
p_ex_info->cpu = smp_processor_id();
snprintf(p_ex_info->task_name,
sizeof(p_ex_info->task_name), "%s", current->comm);
p_ex_info->ktime = local_clock();
snprintf(p_ex_info->pc,
sizeof(p_ex_info->pc), "%pS", pc);
snprintf(p_ex_info->lr,
sizeof(p_ex_info->lr), "%pS", lr);
slen = scnprintf(p_ex_info->panic_buf,
sizeof(p_ex_info->panic_buf), "%s", str);
msg = p_ex_info->panic_buf;
if ((slen >= 1) && (msg[slen - 1] == '\n'))
msg[slen - 1] = '\0';
}
}
#else
static inline void __summary_save_dying_msg_for_user_reset_debug(
const char *str, const void *pc, const void *lr)
{ }
#endif
#ifdef CONFIG_ARM64
#define PT_REGS_PC pc
#define PT_REGS_LR regs[30]
#define ARCH_INSTR_SIZE 0x4
#else
#define PT_REGS_PC ARM_pc
#define PT_REGS_LR ARM_lr
#define ARCH_INSTR_SIZE 0x4
#endif
int sec_debug_summary_save_die_info(const char *str, struct pt_regs *regs)
{
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
sec_delay_check = 0;
#endif
if (secdbg_apss) {
snprintf(secdbg_apss->excp.pc_sym, sizeof(secdbg_apss->excp.pc_sym),
"%pS", (void *)regs->PT_REGS_PC);
snprintf(secdbg_apss->excp.lr_sym, sizeof(secdbg_apss->excp.lr_sym),
"%pS", (void *)regs->PT_REGS_LR);
}
__summary_save_dying_msg_for_user_reset_debug(str,
(void *)regs->PT_REGS_PC, (void *)regs->PT_REGS_LR);
return 0;
}
int sec_debug_summary_save_panic_info(const char *str, unsigned long caller)
{
#ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
sec_delay_check = 0;
#endif
if (secdbg_apss) {
snprintf(secdbg_apss->excp.panic_caller,
sizeof(secdbg_apss->excp.panic_caller), "%pS", (void *)(caller - ARCH_INSTR_SIZE));
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));
}
__summary_save_dying_msg_for_user_reset_debug(str,
(void *)(caller - ARCH_INSTR_SIZE), (void *)caller);
return 0;
}
void sec_debug_summary_set_timeout_subsys(const char *source, const char *dest)
{
if (secdbg_apss == NULL)
return;
if (source)
strlcpy(secdbg_apss->excp.timeout_subsys[0], source,
sizeof(secdbg_apss->excp.timeout_subsys[0]));
if (dest)
strlcpy(secdbg_apss->excp.timeout_subsys[1], dest,
sizeof(secdbg_apss->excp.timeout_subsys[1]));
}
#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
#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];
void sec_debug_save_cpu_freq_voltage(int cpu, int flag, unsigned long value)
{
if (flag == SAVE_FREQ)
cpu_frequency[cpu] = value;
else if (flag == SAVE_VOLT)
cpu_volt[cpu] = (unsigned int)value;
}
#endif
static uint32_t tzapps_start_addr;
static uint32_t tzapps_size;
void __deprecated sec_debug_summary_secure_app_addr_size(uint32_t addr,
uint32_t size)
{
tzapps_start_addr = addr;
tzapps_size = size;
}
void sec_debug_summary_set_lpm_info_cci(uint64_t paddr)
{
#ifdef CONFIG_MSM_PM
if (secdbg_apss) {
pr_info("0x%llx\n", paddr);
secdbg_apss->aplpm.p_cci = paddr;
}
#endif
}
void *sec_debug_summary_get_modem(void)
{
if (secdbg_summary)
return (void *)&secdbg_summary->priv.modem;
pr_warn("secdbg_summary is null.\n");
return NULL;
}
struct sec_debug_summary_data_apss *sec_debug_summary_get_apss(void)
{
if (secdbg_summary)
return &secdbg_summary->priv.apss;
pr_warn("secdbg_summary is null.\n");
return NULL;
}
static void __init summary_add_info_mon(const char *name,
unsigned int size, phys_addr_t pa)
{
struct sec_debug_summary_simple_var_mon *info_mon;
info_mon = &(secdbg_apss->info_mon);
if (info_mon->idx >= ARRAY_SIZE(info_mon->var)) {
pr_warn("index variable is out of bound!\n");
return;
}
strlcpy(info_mon->var[info_mon->idx].name,
name, sizeof(info_mon->var[0].name));
info_mon->var[info_mon->idx].sizeof_type = size;
info_mon->var[info_mon->idx].var_paddr = pa;
info_mon->idx++;
}
#define ADD_VAR_TO_INFOMON(var) \
summary_add_info_mon(#var, sizeof(var), (uint64_t)__pa(&var))
#define ADD_STR_TO_INFOMON(pstr) \
summary_add_info_mon(#pstr, -1, (uint64_t)__pa(pstr))
static void __init summary_add_var_mon(char *name,
unsigned int size, phys_addr_t pa)
{
struct sec_debug_summary_simple_var_mon *var_mon;
if (!secdbg_apss) {
pr_warn("secdbg_apps is not ready!\n");
return;
}
var_mon = &(secdbg_apss->var_mon);
if (var_mon->idx >= ARRAY_SIZE(var_mon->var)) {
pr_warn("index variable is out of bound!\n");
return;
}
strlcpy(var_mon->var[var_mon->idx].name, name,
sizeof(var_mon->var[0].name));
var_mon->var[var_mon->idx].sizeof_type = size;
var_mon->var[var_mon->idx].var_paddr = pa;
var_mon->idx++;
}
#define ADD_VAR_TO_VARMON(var) \
summary_add_var_mon(#var, sizeof(var), __pa(&var))
#define ADD_STR_TO_VARMON(pstr) \
summary_add_var_mon(#pstr, -1, __pa(pstr))
#define VAR_NAME_MAX 30
#define ADD_ARRAY_TO_VARMON(arr, _index, _varname) \
do { \
char name[VAR_NAME_MAX]; \
char _strindex[5]; \
snprintf(_strindex, ARRAY_SIZE(_strindex) - 2, "%c%zu%c", \
'_', _index, '\0'); \
strlcpy(name, #_varname, ARRAY_SIZE(name)); \
strlcat(name, _strindex, ARRAY_SIZE(name)); \
summary_add_var_mon(name, sizeof(arr), __pa(&arr)); \
} while (0)
#define ADD_STR_ARRAY_TO_VARMON(pstrarr, _index, _varname) \
do { \
char name[VAR_NAME_MAX]; \
char _strindex[5]; \
snprintf(_strindex, ARRAY_SIZE(_strindex) - 2, "%c%zu%c", \
'_', _index, '\0'); \
strlcpy(name, #_varname, ARRAY_SIZE(name)); \
strlcat(name, _strindex, ARRAY_SIZE(name)); \
summary_add_var_mon(name, -1, __pa(&pstrarr)); \
} while (0)
#ifdef __SEC_DEBUG_SUMMARY_BUILD_ROOT
#define summary_set_build_root(s) __summary_set_build_root(s)
#define __summary_set_build_root(s) #s
static const char build_root[] =
summary_set_build_root(__SEC_DEBUG_SUMMARY_BUILD_ROOT);
#else
static const char build_root[] = "UNKNOWN";
#endif
static void __init summary_init_infomon(void)
{
ADD_STR_TO_INFOMON(build_root);
ADD_STR_TO_INFOMON(linux_banner);
summary_add_info_mon("Kernel cmdline", -1,
IS_ENABLED(CONFIG_SAMSUNG_PRODUCT_SHIP) ?
__pa(erased_command_line) :
__pa(saved_command_line));
summary_add_info_mon("Hardware name", -1, __pa(sec_debug_arch_desc));
}
#if defined(CONFIG_SEC_DEBUG_SCHED_LOG_PER_CPU)
#define phys_addr_of_secdbg_last_pet_ns_paddr(__member) \
(secdbg_paddr + offsetof(struct sec_debug_last_pet_ns, __member))
#else
#define phys_addr_of_secdbg_paddr(__member) \
(secdbg_paddr + offsetof(struct sec_debug_log, __member))
#endif
#ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML
static void __init __summary_init_varmon_verbose(void)
{
size_t i;
for (i = 0; i < num_present_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);
}
}
#else
static void __init __summary_init_varmon_verbose(void) { }
#endif
static void __init summary_init_varmon(void)
{
uint64_t last_pet_paddr;
uint64_t last_ns_paddr;
#if defined(CONFIG_SEC_DEBUG_SCHED_LOG_PER_CPU)
/* save paddrs of last_pet und last_ns */
if (secdbg_paddr && secdbg_last_pet_ns) {
last_pet_paddr = phys_addr_of_secdbg_last_pet_ns_paddr(last_pet);
summary_add_var_mon("last_pet",
sizeof((secdbg_last_pet_ns->last_pet)), last_pet_paddr);
last_ns_paddr = phys_addr_of_secdbg_last_pet_ns_paddr(last_ns);
summary_add_var_mon("last_ns",
sizeof((secdbg_last_pet_ns->last_ns.counter)),
last_ns_paddr);
} else
pr_emerg("**** secdbg_last_pet_ns or secdbg_paddr is not initialized ****\n");
#else
/* save paddrs of last_pet und last_ns */
if (secdbg_paddr && secdbg_log) {
last_pet_paddr = phys_addr_of_secdbg_paddr(last_pet);
summary_add_var_mon("last_pet",
sizeof((secdbg_log->last_pet)), last_pet_paddr);
last_ns_paddr = phys_addr_of_secdbg_paddr(last_ns);
summary_add_var_mon("last_ns",
sizeof((secdbg_log->last_ns.counter)),
last_ns_paddr);
} else
pr_emerg("**** secdbg_log or secdbg_paddr is not initialized ****\n");
#endif
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0)
ADD_VAR_TO_VARMON(boot_reason);
ADD_VAR_TO_VARMON(cold_boot);
#endif
#endif
__summary_init_varmon_verbose();
}
#if defined(CONFIG_SEC_DEBUG_SCHED_LOG_PER_CPU)
DECLARE_PER_CPU(struct sec_debug_log, sec_debug_log_cpu);
#endif
static void __init summary_init_sched_log(void)
{
struct sec_debug_summary_sched_log *sched_log = &(secdbg_apss->sched_log);
#if defined(CONFIG_SEC_DEBUG_SCHED_LOG_PER_CPU)
sched_log->sched_idx_vaddr = (uint64_t)&sec_debug_log_cpu + offsetof(struct sec_debug_log, sched);
sched_log->sched_buf_vaddr = (uint64_t)&sec_debug_log_cpu + offsetof(struct sec_debug_log, sched) + offsetof(struct sched_log, buf);
sched_log->sched_struct_buf_sz = sizeof(struct sched_buf);
sched_log->sched_struct_log_sz = sizeof(struct sched_log);
sched_log->sched_array_cnt = SCHED_LOG_MAX;
sched_log->irq_idx_vaddr = (uint64_t)&sec_debug_log_cpu + offsetof(struct sec_debug_log, irq);
sched_log->irq_buf_vaddr = (uint64_t)&sec_debug_log_cpu + offsetof(struct sec_debug_log, irq) + offsetof(struct irq_log, buf);
sched_log->irq_struct_buf_sz = sizeof(struct irq_buf);
sched_log->irq_struct_log_sz = sizeof(struct irq_log);
sched_log->irq_array_cnt = SCHED_LOG_MAX;
#ifdef CONFIG_SEC_DEBUG_MSG_LOG
sched_log->msglog_idx_vaddr = (uint64_t)&sec_debug_log_cpu + offsetof(struct sec_debug_log, secmsg);
sched_log->msglog_buf_vaddr = (uint64_t)&sec_debug_log_cpu + offsetof(struct sec_debug_log, secmsg) + offsetof(struct secmsg_log, buf);
sched_log->msglog_struct_buf_sz = sizeof(struct secmsg_buf);
sched_log->msglog_struct_log_sz = sizeof(struct secmsg_log);
sched_log->msglog_array_cnt = MSG_LOG_MAX;
#endif
#else
sched_log->sched_idx_paddr = phys_addr_of_secdbg_paddr(sched[0].idx);
sched_log->sched_buf_paddr = phys_addr_of_secdbg_paddr(sched[0].buf);
sched_log->sched_struct_buf_sz = sizeof(struct sched_buf);
sched_log->sched_struct_log_sz = sizeof(struct sched_log);
sched_log->sched_array_cnt = SCHED_LOG_MAX;
sched_log->irq_idx_paddr = phys_addr_of_secdbg_paddr(irq[0].idx);
sched_log->irq_buf_paddr = phys_addr_of_secdbg_paddr(irq[0].buf);
sched_log->irq_struct_buf_sz = sizeof(struct irq_buf);
sched_log->irq_struct_log_sz = sizeof(struct irq_log);
sched_log->irq_array_cnt = SCHED_LOG_MAX;
sched_log->secure_idx_paddr = phys_addr_of_secdbg_paddr(secure[0].idx);
sched_log->secure_buf_paddr = phys_addr_of_secdbg_paddr(secure[0].buf);
sched_log->secure_struct_buf_sz = sizeof(struct secure_buf);
sched_log->secure_struct_log_sz = sizeof(struct secure_log);
sched_log->secure_array_cnt = TZ_LOG_MAX;
sched_log->irq_exit_idx_paddr = phys_addr_of_secdbg_paddr(irq_exit[0].idx);
sched_log->irq_exit_buf_paddr = phys_addr_of_secdbg_paddr(irq_exit[0].buf);
sched_log->irq_exit_struct_buf_sz = sizeof(struct irq_exit_buf);
sched_log->irq_exit_struct_log_sz = sizeof(struct irq_exit_log);
sched_log->irq_exit_array_cnt = SCHED_LOG_MAX;
#ifdef CONFIG_SEC_DEBUG_MSG_LOG
sched_log->msglog_idx_paddr = phys_addr_of_secdbg_paddr(secmsg[0].idx);
sched_log->msglog_buf_paddr = phys_addr_of_secdbg_paddr(secmsg[0].buf);
sched_log->msglog_struct_buf_sz = sizeof(struct secmsg_buf);
sched_log->msglog_struct_log_sz = sizeof(struct secmsg_log);
sched_log->msglog_array_cnt = MSG_LOG_MAX;
#endif
#endif
}
#ifdef CONFIG_QCOM_MEMORY_DUMP_V2
static struct sec_debug_summary_msm_dump_info msm_dump_info_cached;
void sec_debug_summary_bark_dump(void *s_cpu_data, void *cpu_buf,
uint32_t cpu_ctx_size)
{
msm_dump_info_cached.cpu_data_paddr = (uint64_t)virt_to_phys(s_cpu_data);
msm_dump_info_cached.cpu_buf_paddr = (uint64_t)virt_to_phys(cpu_buf);
msm_dump_info_cached.cpu_ctx_size = cpu_ctx_size;
msm_dump_info_cached.offset = 0x10; /* 16 bytes */
}
static void *get_wdog_regsave_paddr(void)
{
return (void *)__pa(&(msm_dump_info_cached.cpu_buf_paddr));
}
#endif
static void __init summary_init_core_reg(void)
{
struct sec_debug_summary_cpu_context *cpu_reg = &(secdbg_apss->cpu_reg);
/* setup sec debug core reg info */
cpu_reg->sec_debug_core_reg_paddr = virt_to_phys(&sec_debug_core_reg);
pr_info("sec_debug_core_reg_paddr = 0x%llx\n",
cpu_reg->sec_debug_core_reg_paddr);
#ifdef CONFIG_QCOM_MEMORY_DUMP_V2
/* setup qc core reg info */
memcpy(&(cpu_reg->msm_dump_info), &msm_dump_info_cached,
sizeof(struct sec_debug_summary_msm_dump_info));
if (!cpu_reg->msm_dump_info.cpu_data_paddr ||
!cpu_reg->msm_dump_info.cpu_buf_paddr)
pr_warn("msm_dump_info is no initialized correctly!\n");
#endif
}
static void __init summary_init_avc_log(void)
{
#ifdef CONFIG_SEC_DEBUG_AVC_LOG
secdbg_apss->avc_log.secavc_idx_paddr = phys_addr_of_secdbg_paddr(secavc[0].idx);
secdbg_apss->avc_log.secavc_buf_paddr = phys_addr_of_secdbg_paddr(secavc[0].buf);
secdbg_apss->avc_log.secavc_struct_sz = sizeof(struct secavc_buf);
secdbg_apss->avc_log.secavc_array_cnt = AVC_LOG_MAX;
#endif
}
int sec_debug_summary_is_modem_separate_debug_ssr(void)
{
if (IS_ENABLED(CONFIG_SEC_CP_SEPARATE_DEBUG))
return SEC_DEBUG_MODEM_SEPARATE_EN;
else
return secdbg_summary->priv.modem.separate_debug;
}
#define SET_MEMBER_TYPE_INFO(PTR, TYPE, MEMBER) \
{ \
(PTR)->size = sizeof(((TYPE *)0)->MEMBER); \
(PTR)->offset = offsetof(TYPE, MEMBER); \
}
static void __init summary_init_task_info(void)
{
secdbg_apss->task.stack_size = THREAD_SIZE;
secdbg_apss->task.start_sp = THREAD_SIZE;
#ifdef CONFIG_VMAP_STACK
secdbg_apss->task.irq_stack.pcpu_stack = (uint64_t)&irq_stack_ptr;
#else
secdbg_apss->task.irq_stack.pcpu_stack = (uint64_t)&irq_stack;
#endif
secdbg_apss->task.irq_stack.size = IRQ_STACK_SIZE;
secdbg_apss->task.irq_stack.start_sp = IRQ_STACK_SIZE;
secdbg_apss->task.ti.struct_size = sizeof(struct thread_info);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ti.flags, struct thread_info, flags);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.cpu, struct task_struct, cpu);
#if defined (CONFIG_CFP_ROPP) || defined(CONFIG_RKP_CFP_ROPP)
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ti.rrk, struct thread_info, rrk);
#endif
secdbg_apss->task.ts.struct_size = sizeof(struct task_struct);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.state, struct task_struct, state);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.exit_state, struct task_struct,
exit_state);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.stack, struct task_struct, stack);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.flags, struct task_struct, flags);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.on_cpu, struct task_struct, on_cpu);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.pid, struct task_struct, pid);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.comm, struct task_struct, comm);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.tasks_next, struct task_struct,
tasks.next);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.thread_group_next,
struct task_struct, thread_group.next);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.fp, struct task_struct,
thread.cpu_context.fp);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.sp, struct task_struct,
thread.cpu_context.sp);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.pc, struct task_struct,
thread.cpu_context.pc);
#ifdef CONFIG_SCHED_INFO
/* sched_info */
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.sched_info__pcount,
struct task_struct, sched_info.pcount);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.sched_info__run_delay,
struct task_struct,
sched_info.run_delay);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.sched_info__last_arrival,
struct task_struct,
sched_info.last_arrival);
SET_MEMBER_TYPE_INFO(&secdbg_apss->task.ts.sched_info__last_queued,
struct task_struct,
sched_info.last_queued);
#endif
secdbg_apss->task.init_task = (uint64_t)&init_task;
#if defined (CONFIG_CFP_ROPP) || defined(CONFIG_RKP_CFP_ROPP)
secdbg_apss->task.ropp.magic = 0x50504F52;
#else
secdbg_apss->task.ropp.magic = 0x0;
#endif
}
static void __init summary_init_kernel_constant(void)
{
struct sec_debug_summary_kconst *kconst = &(secdbg_apss->kconst);
kconst->nr_cpus = num_possible_cpus();
kconst->per_cpu_offset.pa = virt_to_phys(__per_cpu_offset);
kconst->per_cpu_offset.size = sizeof(__per_cpu_offset[0]);
kconst->per_cpu_offset.count = ARRAY_SIZE(__per_cpu_offset);
kconst->phys_offset = PHYS_OFFSET;
kconst->page_offset = PAGE_OFFSET;
kconst->va_bits = VA_BITS;
kconst->kimage_vaddr = kimage_vaddr;
kconst->kimage_voffset = kimage_voffset;
kconst->swapper_pg_dir_paddr = __pa_symbol(swapper_pg_dir);
kconst->vmap_stack = IS_ENABLED(CONFIG_VMAP_STACK) ? 1 : 0;
}
static void __init summary_set_kallsyms_info(void)
{
struct sec_debug_summary_ksyms *ksyms = &(secdbg_apss->ksyms);
if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE)) {
ksyms->addresses_pa = __pa(kallsyms_addresses);
ksyms->relative_base = 0x0;
ksyms->offsets_pa = 0x0;
} else {
ksyms->addresses_pa = 0x0;
ksyms->relative_base = (uint64_t)kallsyms_relative_base;
ksyms->offsets_pa = __pa(kallsyms_offsets);
}
ksyms->names_pa = __pa(kallsyms_names);
ksyms->num_syms = kallsyms_num_syms;
ksyms->token_table_pa = __pa(kallsyms_token_table);
ksyms->token_index_pa = __pa(kallsyms_token_index);
ksyms->markers_pa = __pa(kallsyms_markers);
ksyms->sect.sinittext = (uintptr_t)_sinittext;
ksyms->sect.einittext = (uintptr_t)_einittext;
ksyms->sect.stext = (uintptr_t)_stext;
ksyms->sect.etext = (uintptr_t)_etext;
ksyms->sect.end = (uintptr_t)_end;
ksyms->kallsyms_all = IS_ENABLED(CONFIG_KALLSYMS_ALL) ? 1 : 0;
ksyms->magic = SEC_DEBUG_SUMMARY_MAGIC1;
}
static void summary_set_lpm_info_runqueues(void)
{
secdbg_apss->aplpm.p_runqueues = virt_to_phys((void *)&runqueues);
#ifdef CONFIG_SCHED_WALT
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0)
secdbg_apss->aplpm.cstate_offset = offsetof(struct rq, cstate);
#endif
#endif
}
#ifdef CONFIG_SCHED_WALT
int __weak get_num_clusters(void)
{
return num_clusters;
}
#endif
static void __init summary_set_lpm_info_cluster(void)
{
#ifdef CONFIG_SCHED_WALT
secdbg_apss->aplpm.num_clusters = get_num_clusters();
secdbg_apss->aplpm.p_cluster = virt_to_phys((void *)sched_cluster);
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0)
secdbg_apss->aplpm.dstate_offset = offsetof(struct sched_cluster, dstate);
#endif
#endif
}
static void __init sec_debug_summary_external_init(void)
{
summary_set_kallsyms_info();
summary_set_lpm_info_cluster();
summary_set_lpm_info_runqueues();
sec_debug_summary_set_klog_info(secdbg_apss);
sec_debug_summary_set_msm_memdump_info(secdbg_apss);
#if IS_ENABLED(CONFIG_QCOM_RTB)
sec_debug_summary_set_rtb_info(secdbg_apss);
#endif
summary_init_coreinfo(secdbg_apss);
}
#if defined(CONFIG_QCOM_SMEM)
static int __init __qcom_smem_alloc_secdbg_summary(size_t size)
{
int err;
/* set summary address in smem for other subsystems to see */
err = qcom_smem_alloc(QCOM_SMEM_HOST_ANY, SMEM_ID_VENDOR2, size);
if (err && err != -EEXIST) {
pr_err("smem alloc failed! (%d)\n", err);
return err;
}
secdbg_summary = qcom_smem_get(QCOM_SMEM_HOST_ANY,
SMEM_ID_VENDOR2, &size);
if (!secdbg_summary || size < sizeof(struct sec_debug_summary)) {
pr_err("smem get failed!\n");
return -ENOMEM;
}
return 0;
}
#else
static inline int __qcom_smem_alloc_secdbg_summary(size_t size) { return -ENODEV; }
#endif
#if defined(CONFIG_MSM_SMEM)
static int __init __msm_smem_alloc_secdbg_summary(size_t size)
{
secdbg_summary = smem_alloc(SMEM_ID_VENDOR2, size, 0, SMEM_ANY_HOST_FLAG);
if (!secdbg_summary) {
pr_err("smem alloc failed!\n");
return -ENOMEM;
}
return 0;
}
#else
static inline int __msm_smem_alloc_secdbg_summary(size_t size) { return -ENODEV; }
#endif
static int __init __smem_alloc_secdbg_summary(size_t size)
{
int err;
err = __qcom_smem_alloc_secdbg_summary(size);
if (unlikely(err) && IS_ENABLED(CONFIG_MSM_SMEM))
err = __msm_smem_alloc_secdbg_summary(size);
return err;
}
static int _sec_debug_summary_init(void)
{
int err;
size_t size = sizeof(struct sec_debug_summary);
pr_info("SMEM_ID_VENDOR2=0x%x size=0x%lx\n",
(unsigned int)SMEM_ID_VENDOR2, size);
err = __smem_alloc_secdbg_summary(size);
if (err) {
pr_err("return with error[%d]", err);
return err;
}
memset_io(secdbg_summary, 0x0, size);
secdbg_summary->_apss =
qcom_smem_virt_to_phys(&secdbg_summary->priv.apss);
secdbg_summary->_rpm =
qcom_smem_virt_to_phys(&secdbg_summary->priv.rpm);
secdbg_summary->_modem =
qcom_smem_virt_to_phys(&secdbg_summary->priv.modem);
secdbg_summary->_dsps =
qcom_smem_virt_to_phys(&secdbg_summary->priv.dsps);
pr_info("apss(%llx) rpm(%llx) modem(%llx) dsps(%llx)\n",
secdbg_summary->_apss,
secdbg_summary->_rpm,
secdbg_summary->_modem,
secdbg_summary->_dsps);
/* FIXME: 'sec_debug_summary_secure_app_addr_size' is not called
* anywhere. It seems that, following two lines are deprecated.
*/
secdbg_summary->secure_app_start_addr = tzapps_start_addr;
secdbg_summary->secure_app_size = tzapps_size;
secdbg_apss = &secdbg_summary->priv.apss;
strlcpy(secdbg_apss->name, "APSS", sizeof(secdbg_apss->name));
strlcpy(secdbg_apss->state, "Init", sizeof(secdbg_apss->state));
secdbg_apss->nr_cpus = num_present_cpus();
secdbg_apss->dump_sink_paddr = get_pa_dump_sink();
secdbg_apss->tz_core_dump = get_wdog_regsave_paddr();
summary_init_debug_arch_desc();
summary_init_infomon();
summary_init_varmon();
summary_init_sched_log();
summary_init_avc_log();
summary_init_core_reg();
summary_init_kernel_constant();
summary_init_task_info();
/* TODO: initialize debug_summary data using external symbols.
* Plz see 'sec_debug_summary_external.h' file to manage
* which files and symbols are used at here.
*/
sec_debug_summary_external_init();
/* 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;
}
#ifdef CONFIG_SEC_DEBUG_SUMMARY_DRIVER
static int sec_debug_summary_probe(struct platform_device *pdev)
{
int err = _sec_debug_summary_init();
if (err) {
pr_err("return with error[%d]", err);
return err;
}
platform_set_drvdata(pdev, secdbg_summary);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id sec_debug_summary_dt_ids[] = {
{ .compatible = "samsung,sec-debug-summary" },
{ }
};
MODULE_DEVICE_TABLE(of, sec_debug_summary_dt_ids);
#endif /* CONFIG_OF */
struct platform_driver sec_debug_summary_driver = {
.probe = sec_debug_summary_probe,
.driver = {
.name = "sec_debug_summary",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = sec_debug_summary_dt_ids,
#endif
},
};
static int __init sec_debug_summary_init(void)
{
int err;
err = platform_driver_register(&sec_debug_summary_driver);
if (err)
pr_err("Failed to register sec_debug_summary platform driver: %d\n", err);
return 0;
}
subsys_initcall_sync(sec_debug_summary_init);
static void __exit sec_debug_summary_exit(void)
{
platform_driver_unregister(&sec_debug_summary_driver);
}
module_exit(sec_debug_summary_exit);
#else
static int __init sec_debug_summary_init(void)
{
return _sec_debug_summary_init();
}
subsys_initcall_sync(sec_debug_summary_init);
#endif