/* * Copyright (c) 2014-2017 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Samsung TN debugging code * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_SEC_DEBUG /* enable/disable sec_debug feature * level = 0 when enable = 0 && enable_user = 0 * level = 1 when enable = 1 && enable_user = 0 * level = 0x10001 when enable = 1 && enable_user = 1 * The other cases are not considered */ union sec_debug_level_t { struct { u16 kernel_fault; u16 user_fault; } en; u32 uint_val; } sec_debug_level = { .en.kernel_fault = 1, }; module_param_named(enable, sec_debug_level.en.kernel_fault, ushort, 0644); module_param_named(enable_user, sec_debug_level.en.user_fault, ushort, 0644); module_param_named(level, sec_debug_level.uint_val, uint, 0644); static int sec_debug_reserve_ok; static int __debug_sj_lock; int sec_debug_check_sj(void) { if (__debug_sj_lock == 1) /* Locked */ return 1; else if (__debug_sj_lock == 0) /* Unlocked */ return 0; return -1; } static int __init sec_debug_get_sj_status(char *str) { unsigned long val = memparse(str, &str); pr_err("%s: start %lx\n", __func__, val); if (!val) { pr_err("%s: UNLOCKED (%lx)\n", __func__, val); __debug_sj_lock = 0; /* Unlocked or Disabled */ return 1; } else { pr_err("%s: LOCKED (%lx)\n", __func__, val); __debug_sj_lock = 1; /* Locked */ return 1; } } __setup("sec_debug.sjl=", sec_debug_get_sj_status); int sec_debug_get_debug_level(void) { return sec_debug_level.uint_val; } static long __force_upload; static int sec_debug_get_force_upload(void) { /* enabled */ if (__force_upload == 1) return 1; /* disabled */ else if (__force_upload == 0) return 0; return -1; } static int __init sec_debug_force_upload(char *str) { unsigned long val = memparse(str, &str); if (!val) { pr_err("%s: disabled (%lx)\n", __func__, val); __force_upload = 0; /* Unlocked or Disabled */ return 1; } else { pr_err("%s: enabled (%lx)\n", __func__, val); __force_upload = 1; /* Locked */ return 1; } } __setup("androidboot.force_upload=", sec_debug_force_upload); int sec_debug_enter_upload(void) { return sec_debug_get_force_upload(); } static void sec_debug_user_fault_dump(void) { if (sec_debug_level.en.kernel_fault == 1 && sec_debug_level.en.user_fault == 1) panic("User Fault"); } static ssize_t sec_debug_user_fault_write(struct file *file, const char __user *buffer, size_t count, loff_t *offs) { char buf[100]; if (count > sizeof(buf) - 1) return -EINVAL; if (copy_from_user(buf, buffer, count)) return -EFAULT; buf[count] = '\0'; if (strncmp(buf, "dump_user_fault", 15) == 0) sec_debug_user_fault_dump(); return count; } static const struct file_operations sec_debug_user_fault_proc_fops = { .write = sec_debug_user_fault_write, }; static int __init sec_debug_user_fault_init(void) { struct proc_dir_entry *entry; entry = proc_create("user_fault", S_IWUSR | S_IWGRP, NULL, &sec_debug_user_fault_proc_fops); if (!entry) return -ENOMEM; return 0; } device_initcall(sec_debug_user_fault_init); /* layout of SDRAM : First 4KB of DRAM * 0x0: magic (4B) * 0x4~0x3FF: panic string (1020B) * 0x400~0x7FF: panic Extra Info (1KB) * 0x800~0xFFB: panic dumper log (2KB - 4B) * 0xFFC: copy of magic (4B) */ enum sec_debug_upload_magic_t { UPLOAD_MAGIC_INIT = 0x0, UPLOAD_MAGIC_PANIC = 0x66262564, }; enum sec_debug_upload_cause_t { UPLOAD_CAUSE_INIT = 0xCAFEBABE, UPLOAD_CAUSE_KERNEL_PANIC = 0x000000C8, UPLOAD_CAUSE_FORCED_UPLOAD = 0x00000022, UPLOAD_CAUSE_USER_FORCED_UPLOAD = 0x00000074, UPLOAD_CAUSE_CP_ERROR_FATAL = 0x000000CC, UPLOAD_CAUSE_USER_FAULT = 0x0000002F, UPLOAD_CAUSE_HSIC_DISCONNECTED = 0x000000DD, UPLOAD_CAUSE_POWERKEY_LONG_PRESS = 0x00000085, UPLOAD_CAUSE_HARD_RESET = 0x00000066, }; static void sec_debug_set_upload_magic(unsigned magic, char *str) { preempt_disable(); *(unsigned int *)SEC_DEBUG_MAGIC_VA = magic; *(unsigned int *)(SEC_DEBUG_MAGIC_VA + SZ_4K - 4) = magic; if (str) { strncpy((char *)SEC_DEBUG_MAGIC_VA + 4, str, SZ_1K - 4); #ifdef CONFIG_SEC_DEBUG_EXTRA_INFO sec_debug_set_extra_info_panic(str); sec_debug_finish_extra_info(); #endif } flush_cache_all(); preempt_enable(); pr_emerg("sec_debug: set magic code (0x%x)\n", magic); } static void sec_debug_set_upload_cause(enum sec_debug_upload_cause_t type) { exynos_pmu_write(SEC_DEBUG_PANIC_INFORM, type); pr_emerg("sec_debug: set upload cause (0x%x)\n", type); } static void sec_debug_kmsg_dump(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason) { kmsg_dump_get_buffer(dumper, true, (char *)SEC_DEBUG_DUMPER_LOG_VA, SZ_2K - 4, NULL); } static struct kmsg_dumper sec_dumper = { .dump = sec_debug_kmsg_dump, }; static int __init sec_debug_magic_setup(struct reserved_mem *rmem) { pr_info("%s: Reserved Mem(0x%llx, 0x%llx) - Success\n", __func__, rmem->base, rmem->size); sec_debug_reserve_ok = 1; return 0; } RESERVEDMEM_OF_DECLARE(sec_debug_magic, "exynos,sec_debug_magic", sec_debug_magic_setup); #define MAX_RECOVERY_CAUSE_SIZE 256 char recovery_cause[MAX_RECOVERY_CAUSE_SIZE]; unsigned long recovery_cause_offset; static ssize_t show_recovery_cause(struct device *dev, struct device_attribute *attr, char *buf) { if (!recovery_cause_offset) return 0; sec_get_param_str(recovery_cause_offset, buf); pr_info("%s: %s\n", __func__, buf); return strlen(buf); } static ssize_t store_recovery_cause(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { if (!recovery_cause_offset) return 0; if (strlen(buf) > sizeof(recovery_cause)) pr_err("%s: input buffer length is out of range.\n", __func__); snprintf(recovery_cause, sizeof(recovery_cause), "%s:%d ", current->comm, task_pid_nr(current)); if (strlen(recovery_cause) + strlen(buf) >= sizeof(recovery_cause)) { pr_err("%s: input buffer length is out of range.\n", __func__); return count; } strncat(recovery_cause, buf, strlen(buf)); sec_set_param_str(recovery_cause_offset, recovery_cause, sizeof(recovery_cause)); pr_info("%s: %s, count:%d\n", __func__, recovery_cause, (int)count); return count; } static DEVICE_ATTR(recovery_cause, 0660, show_recovery_cause, store_recovery_cause); void sec_debug_recovery_reboot(void) { char *buf; if (recovery_cause_offset) { if (!recovery_cause[0] || !strlen(recovery_cause)) { buf = "empty caller"; store_recovery_cause(NULL, NULL, buf, strlen(buf)); } } } static int __init sec_debug_recovery_cause_setup(char *str) { recovery_cause_offset = memparse(str, &str); /* If we encounter any problem parsing str ... */ if (!recovery_cause_offset) { pr_err("%s: failed to parse address.\n", __func__); goto out; } pr_info("%s, recovery_cause_offset :%lx\n", __func__, recovery_cause_offset); out: return 0; } __setup("androidboot.recovery_offset=", sec_debug_recovery_cause_setup); static int __init sec_debug_recovery_cause_init(void) { struct device *dev; memset(recovery_cause, 0, MAX_RECOVERY_CAUSE_SIZE); dev = sec_device_create(NULL, "sec_debug"); WARN_ON(!dev); if (IS_ERR(dev)) pr_err("%s:Failed to create devce\n", __func__); if (device_create_file(dev, &dev_attr_recovery_cause) < 0) pr_err("%s: Failed to create device file\n", __func__); return 0; } late_initcall(sec_debug_recovery_cause_init); static int __init sec_debug_init(void) { if (!sec_debug_reserve_ok) pr_crit("fatal: %s: memory has not been reserved\n", __func__); /* clear traps info */ memset((void *)SEC_DEBUG_MAGIC_VA + 4, 0, SZ_1K - 4); sec_debug_set_upload_magic(UPLOAD_MAGIC_PANIC, NULL); kmsg_dump_register(&sec_dumper); return 0; } early_initcall(sec_debug_init); #ifndef arch_irq_stat_cpu #define arch_irq_stat_cpu(cpu) 0 #endif #ifndef arch_irq_stat #define arch_irq_stat() 0 #endif #ifdef arch_idle_time static cputime64_t get_idle_time(int cpu) { cputime64_t idle; idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE]; if (cpu_online(cpu) && !nr_iowait_cpu(cpu)) idle += arch_idle_time(cpu); return idle; } static cputime64_t get_iowait_time(int cpu) { cputime64_t iowait; iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT]; if (cpu_online(cpu) && nr_iowait_cpu(cpu)) iowait += arch_idle_time(cpu); return iowait; } #else static u64 get_idle_time(int cpu) { u64 idle, idle_time = -1ULL; if (cpu_online(cpu)) idle_time = get_cpu_idle_time_us(cpu, NULL); if (idle_time == -1ULL) /* !NO_HZ or cpu offline so we can rely on cpustat.idle */ idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE]; else idle = idle_time * NSEC_PER_USEC; return idle; } static u64 get_iowait_time(int cpu) { u64 iowait, iowait_time = -1ULL; if (cpu_online(cpu)) iowait_time = get_cpu_iowait_time_us(cpu, NULL); if (iowait_time == -1ULL) /* !NO_HZ or cpu offline so we can rely on cpustat.iowait */ iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT]; else iowait = iowait_time * NSEC_PER_USEC; return iowait; } #endif static void sec_debug_dump_cpu_stat(void) { int i, j; u64 user = 0; u64 nice = 0; u64 system = 0; u64 idle = 0; u64 iowait = 0; u64 irq = 0; u64 softirq = 0; u64 steal = 0; u64 guest = 0; u64 guest_nice = 0; u64 sum = 0; u64 sum_softirq = 0; unsigned int per_softirq_sums[NR_SOFTIRQS] = {0}; char *softirq_to_name[NR_SOFTIRQS] = { "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "BLOCK_IOPOLL", "TASKLET", "SCHED", "HRTIMER", "RCU" }; for_each_possible_cpu(i) { user += kcpustat_cpu(i).cpustat[CPUTIME_USER]; nice += kcpustat_cpu(i).cpustat[CPUTIME_NICE]; system += kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; idle += get_idle_time(i); iowait += get_iowait_time(i); irq += kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; softirq += kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]; steal += kcpustat_cpu(i).cpustat[CPUTIME_STEAL]; guest += kcpustat_cpu(i).cpustat[CPUTIME_GUEST]; guest_nice += kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]; sum += kstat_cpu_irqs_sum(i); sum += arch_irq_stat_cpu(i); for (j = 0; j < NR_SOFTIRQS; j++) { unsigned int softirq_stat = kstat_softirqs_cpu(j, i); per_softirq_sums[j] += softirq_stat; sum_softirq += softirq_stat; } } sum += arch_irq_stat(); pr_info("\n"); pr_info("cpu user:%llu \tnice:%llu \tsystem:%llu \tidle:%llu \tiowait:%llu \tirq:%llu \tsoftirq:%llu \t %llu %llu %llu\n", (unsigned long long)nsec_to_clock_t(user), (unsigned long long)nsec_to_clock_t(nice), (unsigned long long)nsec_to_clock_t(system), (unsigned long long)nsec_to_clock_t(idle), (unsigned long long)nsec_to_clock_t(iowait), (unsigned long long)nsec_to_clock_t(irq), (unsigned long long)nsec_to_clock_t(softirq), (unsigned long long)nsec_to_clock_t(steal), (unsigned long long)nsec_to_clock_t(guest), (unsigned long long)nsec_to_clock_t(guest_nice)); pr_info("-------------------------------------------------------------------------------------------------------------\n"); for_each_possible_cpu(i) { /* Copy values here to work around gcc-2.95.3, gcc-2.96 */ user = kcpustat_cpu(i).cpustat[CPUTIME_USER]; nice = kcpustat_cpu(i).cpustat[CPUTIME_NICE]; system = kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]; idle = get_idle_time(i); iowait = get_iowait_time(i); irq = kcpustat_cpu(i).cpustat[CPUTIME_IRQ]; softirq = kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]; steal = kcpustat_cpu(i).cpustat[CPUTIME_STEAL]; guest = kcpustat_cpu(i).cpustat[CPUTIME_GUEST]; guest_nice = kcpustat_cpu(i).cpustat[CPUTIME_GUEST_NICE]; pr_info("cpu%d user:%llu \tnice:%llu \tsystem:%llu \tidle:%llu \tiowait:%llu \tirq:%llu \tsoftirq:%llu \t %llu %llu %llu\n", i, (unsigned long long)nsec_to_clock_t(user), (unsigned long long)nsec_to_clock_t(nice), (unsigned long long)nsec_to_clock_t(system), (unsigned long long)nsec_to_clock_t(idle), (unsigned long long)nsec_to_clock_t(iowait), (unsigned long long)nsec_to_clock_t(irq), (unsigned long long)nsec_to_clock_t(softirq), (unsigned long long)nsec_to_clock_t(steal), (unsigned long long)nsec_to_clock_t(guest), (unsigned long long)nsec_to_clock_t(guest_nice)); } pr_info("-------------------------------------------------------------------------------------------------------------\n"); pr_info("\n"); pr_info("irq : %llu", (unsigned long long)sum); pr_info("-------------------------------------------------------------------------------------------------------------\n"); /* sum again ? it could be updated? */ for_each_irq_nr(j) { unsigned int irq_stat = kstat_irqs(j); if (irq_stat) { pr_info("irq-%-4d : %8u %s\n", j, irq_stat, irq_to_desc(j)->action ? irq_to_desc(j)->action->name ? : "???" : "???"); } } pr_info("-------------------------------------------------------------------------------------------------------------\n"); pr_info("\n"); pr_info("softirq : %llu", (unsigned long long)sum_softirq); pr_info("-------------------------------------------------------------------------------------------------------------\n"); for (i = 0; i < NR_SOFTIRQS; i++) if (per_softirq_sums[i]) pr_info("softirq-%d : %8u %s\n", i, per_softirq_sums[i], softirq_to_name[i]); pr_info("-------------------------------------------------------------------------------------------------------------\n"); } void sec_debug_clear_magic_rambase(void) { /* Clear magic code in normal reboot */ sec_debug_set_upload_magic(UPLOAD_MAGIC_INIT, NULL); } void sec_debug_reboot_handler(void *p) { pr_emerg("sec_debug: %s\n", __func__); /* Clear magic code in normal reboot */ sec_debug_set_upload_magic(UPLOAD_MAGIC_INIT, NULL); if (p != NULL) if (!strcmp(p, "recovery")) sec_debug_recovery_reboot(); } void sec_debug_panic_handler(void *buf, bool dump) { pr_emerg("sec_debug: %s\n", __func__); /* Set upload cause */ sec_debug_set_upload_magic(UPLOAD_MAGIC_PANIC, buf); if (!strncmp(buf, "User Fault", 10)) sec_debug_set_upload_cause(UPLOAD_CAUSE_USER_FAULT); else if (is_hard_reset_occurred()) sec_debug_set_upload_cause(UPLOAD_CAUSE_HARD_RESET); else if (!strncmp(buf, "Crash Key", 9)) sec_debug_set_upload_cause(UPLOAD_CAUSE_FORCED_UPLOAD); else if (!strncmp(buf, "User Crash Key", 14)) sec_debug_set_upload_cause(UPLOAD_CAUSE_USER_FORCED_UPLOAD); else if (!strncmp(buf, "CP Crash", 8)) sec_debug_set_upload_cause(UPLOAD_CAUSE_CP_ERROR_FATAL); else if (!strncmp(buf, "HSIC Disconnected", 17)) sec_debug_set_upload_cause(UPLOAD_CAUSE_HSIC_DISCONNECTED); else sec_debug_set_upload_cause(UPLOAD_CAUSE_KERNEL_PANIC); /* dump debugging info */ if (dump) { sec_debug_dump_cpu_stat(); debug_show_all_locks(); } } void sec_debug_post_panic_handler(void) { hard_reset_delay(); /* reset */ pr_emerg("sec_debug: %s\n", linux_banner); pr_emerg("sec_debug: rebooting...\n"); flush_cache_all(); } #ifdef CONFIG_SEC_DEBUG_FILE_LEAK void sec_debug_print_file_list(void) { int i = 0; unsigned int count = 0; struct file *file = NULL; struct files_struct *files = current->files; const char *p_rootname = NULL; const char *p_filename = NULL; count = files->fdt->max_fds; pr_err("[Opened file list of process %s(PID:%d, TGID:%d) :: %d]\n", current->group_leader->comm, current->pid, current->tgid, count); for (i = 0; i < count; i++) { rcu_read_lock(); file = fcheck_files(files, i); p_rootname = NULL; p_filename = NULL; if (file) { if (file->f_path.mnt && file->f_path.mnt->mnt_root && file->f_path.mnt->mnt_root->d_name.name) p_rootname = file->f_path.mnt->mnt_root->d_name.name; if (file->f_path.dentry && file->f_path.dentry->d_name.name) p_filename = file->f_path.dentry->d_name.name; pr_err("[%04d]%s%s\n", i, p_rootname ? p_rootname : "null", p_filename ? p_filename : "null"); } rcu_read_unlock(); } } void sec_debug_EMFILE_error_proc(unsigned long files_addr) { if (files_addr != (unsigned long)(current->files)) { pr_err("Too many open files Error at %pS\n" "%s(%d) thread of %s process tried fd allocation by proxy.\n" "files_addr = 0x%lx, current->files=0x%p\n", __builtin_return_address(0), current->comm, current->tgid, current->group_leader->comm, files_addr, current->files); return; } pr_err("Too many open files(%d:%s) at %pS\n", current->tgid, current->group_leader->comm, __builtin_return_address(0)); if (!sec_debug_level.en.kernel_fault) return; /* We check EMFILE error in only "system_server","mediaserver" and "surfaceflinger" process.*/ if (!strcmp(current->group_leader->comm, "system_server") || !strcmp(current->group_leader->comm, "mediaserver") || !strcmp(current->group_leader->comm, "surfaceflinger")) { sec_debug_print_file_list(); panic("Too many open files"); } } #endif /* CONFIG_SEC_DEBUG_FILE_LEAK */ static struct sec_debug_next *sdn; static unsigned long sec_debug_next_phys; static unsigned long sec_debug_next_size; void sec_debug_set_task_in_pm_suspend(uint64_t task) { if (sdn) sdn->kernd.task_in_pm_suspend = task; } void sec_debug_set_task_in_sys_reboot(uint64_t task) { if (sdn) sdn->kernd.task_in_sys_reboot = task; } struct watchdogd_info *sec_debug_get_wdd_info(void) { if (sdn) { pr_crit("%s: return right value\n", __func__); return &(sdn->kernd.wddinfo); } pr_crit("%s: return NULL\n", __func__); return NULL; } struct bad_stack_info *sec_debug_get_bs_info(void) { if (sdn) return &sdn->kernd.bsi; return NULL; } void *sec_debug_get_debug_base(int type) { if (sdn) { if (type == SDN_MAP_AUTO_COMMENT) return &(sdn->auto_comment); else if (type == SDN_MAP_EXTRA_INFO) return &(sdn->extra_info); } pr_crit("%s: return NULL\n", __func__); return NULL; } unsigned long sec_debug_get_buf_base(int type) { if (sdn) { return sdn->map.buf[type].base; } pr_crit("%s: return 0\n", __func__); return 0; } unsigned long sec_debug_get_buf_size(int type) { if (sdn) { return sdn->map.buf[type].size; } pr_crit("%s: return 0\n", __func__); return 0; } void sec_debug_set_task_in_sys_shutdown(uint64_t task) { if (sdn) sdn->kernd.task_in_sys_shutdown = task; } void sec_debug_set_task_in_dev_shutdown(uint64_t task) { if (sdn) sdn->kernd.task_in_dev_shutdown = task; } void sec_debug_set_sysrq_crash(struct task_struct *task) { if (sdn) { sdn->kernd.task_in_sysrq_crash = (uint64_t)task; #ifdef CONFIG_SEC_DEBUG_SYSRQ_KMSG if (task) { if (strcmp(task->comm, "init") == 0) sdn->kernd.sysrq_ptr = sec_debug_get_curr_init_ptr(); else sdn->kernd.sysrq_ptr = dbg_snapshot_get_curr_ptr_for_sysrq(); } #endif } } void sec_debug_set_task_in_soft_lockup(uint64_t task) { if (sdn) sdn->kernd.task_in_soft_lockup = task; } void sec_debug_set_cpu_in_soft_lockup(uint64_t cpu) { if (sdn) sdn->kernd.cpu_in_soft_lockup = cpu; } void sec_debug_set_task_in_hard_lockup(uint64_t task) { if (sdn) sdn->kernd.task_in_hard_lockup = task; } void sec_debug_set_cpu_in_hard_lockup(uint64_t cpu) { if (sdn) sdn->kernd.cpu_in_hard_lockup = cpu; } void sec_debug_set_unfrozen_task(uint64_t task) { if (sdn) sdn->kernd.unfrozen_task = task; } void sec_debug_set_unfrozen_task_count(uint64_t count) { if (sdn) sdn->kernd.unfrozen_task_count = count; } void sec_debug_set_task_in_sync_irq(uint64_t task, unsigned int irq, const char *name, struct irq_desc *desc) { if (sdn) { sdn->kernd.sync_irq_task = task; sdn->kernd.sync_irq_num = irq; sdn->kernd.sync_irq_name = (uint64_t)name; sdn->kernd.sync_irq_desc = (uint64_t)desc; if (desc) { sdn->kernd.sync_irq_threads_active = desc->threads_active.counter; if (desc->action && (desc->action->irq == irq) && desc->action->thread) sdn->kernd.sync_irq_thread = (uint64_t)(desc->action->thread); else sdn->kernd.sync_irq_thread = 0; } } } void sec_debug_set_device_shutdown_timeinfo(uint64_t start, uint64_t end, uint64_t duration, uint64_t func) { if (sdn && func) { if (duration > sdn->kernd.dev_shutdown_duration) { sdn->kernd.dev_shutdown_start = start; sdn->kernd.dev_shutdown_end = end; sdn->kernd.dev_shutdown_duration = duration; sdn->kernd.dev_shutdown_func = func; } } } void sec_debug_clr_device_shutdown_timeinfo(void) { if (sdn) { sdn->kernd.dev_shutdown_start = 0; sdn->kernd.dev_shutdown_end = 0; sdn->kernd.dev_shutdown_duration = 0; sdn->kernd.dev_shutdown_func = 0; } } void sec_debug_set_shutdown_device(const char *fname, const char *dname) { if (sdn) { sdn->kernd.sdi.shutdown_func = (uint64_t)fname; sdn->kernd.sdi.shutdown_device = (uint64_t)dname; } } void sec_debug_set_suspend_device(const char *fname, const char *dname) { if (sdn) { sdn->kernd.sdi.suspend_func = (uint64_t)fname; sdn->kernd.sdi.suspend_device = (uint64_t)dname; } } static void init_ess_info(unsigned int index, char *key) { struct ess_info_offset *p; p = &(sdn->ss_info.item[index]); sec_debug_get_kevent_info(p, index); memset(p->key, 0, SD_ESSINFO_KEY_SIZE); snprintf(p->key, SD_ESSINFO_KEY_SIZE, key); } static void sec_debug_set_essinfo(void) { unsigned int index = 0; memset(&(sdn->ss_info), 0, sizeof(struct sec_debug_ess_info)); init_ess_info(index++, "kevnt-task"); init_ess_info(index++, "kevnt-work"); init_ess_info(index++, "kevnt-irq"); init_ess_info(index++, "kevnt-freq"); init_ess_info(index++, "kevnt-idle"); init_ess_info(index++, "kevnt-thrm"); init_ess_info(index++, "kevnt-acpm"); for (; index < SD_NR_ESSINFO_ITEMS;) init_ess_info(index++, "empty"); for (index = 0; index < SD_NR_ESSINFO_ITEMS; index++) printk("%s: key: %s offset: %llx nr: %x\n", __func__, sdn->ss_info.item[index].key, sdn->ss_info.item[index].base, sdn->ss_info.item[index].nr); } static void sec_debug_set_taskinfo(void) { sdn->task.stack_size = THREAD_SIZE; sdn->task.start_sp = THREAD_START_SP; sdn->task.irq_stack.pcpu_stack = (uint64_t)&irq_stack_ptr; sdn->task.irq_stack.size = IRQ_STACK_SIZE; sdn->task.irq_stack.start_sp = IRQ_STACK_START_SP; sdn->task.ti.struct_size = sizeof(struct thread_info); SET_MEMBER_TYPE_INFO(&sdn->task.ti.flags, struct thread_info, flags); SET_MEMBER_TYPE_INFO(&sdn->task.ts.cpu, struct task_struct, cpu); sdn->task.ts.struct_size = sizeof(struct task_struct); SET_MEMBER_TYPE_INFO(&sdn->task.ts.state, struct task_struct, state); SET_MEMBER_TYPE_INFO(&sdn->task.ts.exit_state, struct task_struct, exit_state); SET_MEMBER_TYPE_INFO(&sdn->task.ts.stack, struct task_struct, stack); SET_MEMBER_TYPE_INFO(&sdn->task.ts.flags, struct task_struct, flags); SET_MEMBER_TYPE_INFO(&sdn->task.ts.on_cpu, struct task_struct, on_cpu); SET_MEMBER_TYPE_INFO(&sdn->task.ts.pid, struct task_struct, pid); SET_MEMBER_TYPE_INFO(&sdn->task.ts.on_rq, struct task_struct, on_rq); SET_MEMBER_TYPE_INFO(&sdn->task.ts.comm, struct task_struct, comm); SET_MEMBER_TYPE_INFO(&sdn->task.ts.tasks_next, struct task_struct, tasks.next); SET_MEMBER_TYPE_INFO(&sdn->task.ts.thread_group_next, struct task_struct, thread_group.next); SET_MEMBER_TYPE_INFO(&sdn->task.ts.fp, struct task_struct, thread.cpu_context.fp); SET_MEMBER_TYPE_INFO(&sdn->task.ts.sp, struct task_struct, thread.cpu_context.sp); SET_MEMBER_TYPE_INFO(&sdn->task.ts.pc, struct task_struct, thread.cpu_context.pc); SET_MEMBER_TYPE_INFO(&sdn->task.ts.sched_info__pcount, struct task_struct, sched_info.pcount); SET_MEMBER_TYPE_INFO(&sdn->task.ts.sched_info__run_delay, struct task_struct, sched_info.run_delay); SET_MEMBER_TYPE_INFO(&sdn->task.ts.sched_info__last_arrival, struct task_struct, sched_info.last_arrival); SET_MEMBER_TYPE_INFO(&sdn->task.ts.sched_info__last_queued, struct task_struct, sched_info.last_queued); #ifdef CONFIG_SEC_DEBUG_DTASK SET_MEMBER_TYPE_INFO(&sdn->task.ts.ssdbg_wait__type, struct task_struct, ssdbg_wait.type); SET_MEMBER_TYPE_INFO(&sdn->task.ts.ssdbg_wait__data, struct task_struct, ssdbg_wait.data); #endif /* CONFIG_SEC_DEBUG_DTASK */ sdn->task.init_task = (uint64_t)&init_task; } static void sec_debug_set_spinlockinfo(void) { #ifdef CONFIG_DEBUG_SPINLOCK SET_MEMBER_TYPE_INFO(&sdn->rlock.owner_cpu, struct raw_spinlock, owner_cpu); SET_MEMBER_TYPE_INFO(&sdn->rlock.owner, struct raw_spinlock, owner); sdn->rlock.debug_enabled = 1; #else sdn->rlock.debug_enabled = 0; #endif } static unsigned long kconfig_base; static unsigned long kconfig_size; void sec_debug_set_kconfig(unsigned long base, unsigned long size) { if (!sdn) { pr_info("%s: call before sdn init\n", __func__); } kconfig_base = base; kconfig_size = size; } static void sec_debug_set_kconstants(void) { sdn->kcnst.nr_cpus = NR_CPUS; sdn->kcnst.per_cpu_offset.pa = virt_to_phys(__per_cpu_offset); sdn->kcnst.per_cpu_offset.size = sizeof(__per_cpu_offset[0]); sdn->kcnst.per_cpu_offset.count = sizeof(__per_cpu_offset) / sizeof(__per_cpu_offset[0]); sdn->kcnst.phys_offset = PHYS_OFFSET; sdn->kcnst.phys_mask = PHYS_MASK; sdn->kcnst.page_offset = PAGE_OFFSET; sdn->kcnst.page_mask = PAGE_MASK; sdn->kcnst.page_shift = PAGE_SHIFT; sdn->kcnst.va_bits = VA_BITS; sdn->kcnst.kimage_vaddr = kimage_vaddr; sdn->kcnst.kimage_voffset = kimage_voffset; sdn->kcnst.pa_swapper = (uint64_t)virt_to_phys(init_mm.pgd); sdn->kcnst.pgdir_shift = PGDIR_SHIFT; sdn->kcnst.pud_shift = PUD_SHIFT; sdn->kcnst.pmd_shift = PMD_SHIFT; sdn->kcnst.ptrs_per_pgd = PTRS_PER_PGD; sdn->kcnst.ptrs_per_pud = PTRS_PER_PUD; sdn->kcnst.ptrs_per_pmd = PTRS_PER_PMD; sdn->kcnst.ptrs_per_pte = PTRS_PER_PTE; sdn->kcnst.kconfig_base = kconfig_base; sdn->kcnst.kconfig_size = kconfig_size; sdn->kcnst.pa_text = virt_to_phys(_text); sdn->kcnst.pa_start_rodata = virt_to_phys(__start_rodata); } static void __init sec_debug_init_sdn(struct sec_debug_next *d) { #define clear_sdn_field(__p, __m) memset(&(__p)->__m, 0x0, sizeof((__p)->__m)); clear_sdn_field(d, memtab); clear_sdn_field(d, ksyms); clear_sdn_field(d, kcnst); clear_sdn_field(d, task); clear_sdn_field(d, ss_info); clear_sdn_field(d, rlock); clear_sdn_field(d, kernd); } static int __init sec_debug_next_init(void) { if (!sdn) { pr_info("%s: sdn is not allocated, quit\n", __func__); return -1; } /* set magic */ sdn->magic[0] = SEC_DEBUG_MAGIC0; sdn->magic[1] = SEC_DEBUG_MAGIC1; sdn->version[1] = SEC_DEBUG_KERNEL_UPPER_VERSION << 16; sdn->version[1] += SEC_DEBUG_KERNEL_LOWER_VERSION; /* set member table */ secdbg_base_set_memtab_info(&sdn->memtab); /* set kernel symbols */ sec_debug_set_kallsyms_info(&(sdn->ksyms), SEC_DEBUG_MAGIC1); /* set kernel constants */ sec_debug_set_kconstants(); sec_debug_set_taskinfo(); sec_debug_set_essinfo(); sec_debug_set_spinlockinfo(); sec_debug_set_task_in_pm_suspend((uint64_t)NULL); sec_debug_set_task_in_sys_reboot((uint64_t)NULL); sec_debug_set_task_in_sys_shutdown((uint64_t)NULL); sec_debug_set_task_in_dev_shutdown((uint64_t)NULL); sec_debug_set_sysrq_crash(NULL); sec_debug_set_task_in_soft_lockup((uint64_t)NULL); sec_debug_set_cpu_in_soft_lockup((uint64_t)0); sec_debug_set_task_in_hard_lockup((uint64_t)NULL); sec_debug_set_cpu_in_hard_lockup((uint64_t)0); sec_debug_set_unfrozen_task((uint64_t)NULL); sec_debug_set_unfrozen_task_count((uint64_t)0); sec_debug_set_task_in_sync_irq((uint64_t)NULL, 0, NULL, NULL); sec_debug_clr_device_shutdown_timeinfo(); return 0; } late_initcall(sec_debug_next_init); static int __init sec_debug_next_setup(char *str) { unsigned long size = memparse(str, &str); unsigned long base = 0; /* If we encounter any problem parsing str ... */ if (!size || *str != '@' || kstrtoul(str + 1, 0, &base)) { pr_err("%s: failed to parse address.\n", __func__); goto out; } #ifdef CONFIG_NO_BOOTMEM if (memblock_is_region_reserved(base, size) || memblock_reserve(base, size)) { #else if reserve_bootmem(base, size, BOOTMEM_EXCLUSIVE) { #endif /* size is not match with -size and size + sizeof(...) */ pr_err("%s: failed to reserve size:0x%lx at base 0x%lx\n", __func__, size, base); goto out; } sdn = (struct sec_debug_next *)phys_to_virt(base); if (!sdn) { pr_info("%s: fail to init sec debug next buffer\n", __func__); goto out; } sec_debug_init_sdn(sdn); sec_debug_next_phys = base; sec_debug_next_size = size; pr_info("%s: base(virt):0x%lx size:0x%lx\n", __func__, (unsigned long)sdn, size); pr_info("%s: ds size: 0x%lx\n", __func__, round_up(sizeof(struct sec_debug_next), PAGE_SIZE)); out: return 0; } __setup("sec_debug_next=", sec_debug_next_setup); #endif /* CONFIG_SEC_DEBUG */