[RAMEN9610-21208]coredump: fix race condition between mmget_not_zero()/get_task_mm...
[GitHub/MotorolaMobilityLLC/kernel-slsi.git] / lib / debug-snapshot-helper.c
CommitLineData
dd101ca5
DC
1/*
2 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
3 * http://www.samsung.com
4 *
5 * Debug-SnapShot: Debug Framework for Ramdump based debugging method
6 * The original code is Exynos-Snapshot for Exynos SoC
7 *
8 * Author: Hosung Kim <hosung0.kim@samsung.com>
9 * Author: Changki Kim <changki.kim@samsung.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 */
15
16#include <linux/kernel.h>
17#include <linux/io.h>
18#include <linux/notifier.h>
19#include <linux/reboot.h>
20#include <linux/delay.h>
21#include <linux/kallsyms.h>
22#include <linux/input.h>
23#include <linux/smc.h>
24#include <linux/bitops.h>
25#include <linux/sched/clock.h>
26#include <linux/sched/debug.h>
27#include <linux/nmi.h>
28#include <linux/init_task.h>
29#include <linux/ftrace.h>
30
31#include <asm/cputype.h>
32#include <asm/smp_plat.h>
33#include <asm/core_regs.h>
dd101ca5
DC
34
35#include "debug-snapshot-local.h"
36#include <linux/debug-snapshot-helper.h>
37
38static void dbg_snapshot_soc_dummy_func(void *dummy) {return;}
39static int dbg_snapshot_soc_dummy_func_int(void *dummy) {return 0;}
40static int dbg_snapshot_soc_dummy_func_smc(unsigned long dummy1,
41 unsigned long dummy2,
42 unsigned long dummy3,
43 unsigned long dummy4) {return 0;}
44
45static struct dbg_snapshot_helper_ops dss_soc_dummy_ops = {
46 .soc_early_panic = dbg_snapshot_soc_dummy_func,
47 .soc_prepare_panic_entry = dbg_snapshot_soc_dummy_func,
48 .soc_prepare_panic_exit = dbg_snapshot_soc_dummy_func,
dd101ca5
DC
49 .soc_post_panic_entry = dbg_snapshot_soc_dummy_func,
50 .soc_post_panic_exit = dbg_snapshot_soc_dummy_func,
6b666fd8
HK
51 .soc_post_reboot_entry = dbg_snapshot_soc_dummy_func,
52 .soc_post_reboot_exit = dbg_snapshot_soc_dummy_func,
53 .soc_save_context_entry = dbg_snapshot_soc_dummy_func,
54 .soc_save_context_exit = dbg_snapshot_soc_dummy_func,
55 .soc_save_core = dbg_snapshot_soc_dummy_func,
56 .soc_save_system = dbg_snapshot_soc_dummy_func,
57 .soc_dump_info = dbg_snapshot_soc_dummy_func,
dd101ca5
DC
58 .soc_start_watchdog = dbg_snapshot_soc_dummy_func,
59 .soc_expire_watchdog = dbg_snapshot_soc_dummy_func,
60 .soc_stop_watchdog = dbg_snapshot_soc_dummy_func,
61 .soc_kick_watchdog = dbg_snapshot_soc_dummy_func,
62 .soc_is_power_cpu = dbg_snapshot_soc_dummy_func_int,
63 .soc_smc_call = dbg_snapshot_soc_dummy_func_smc,
64};
65
66struct dbg_snapshot_helper_ops *dss_soc_ops;
67
68void __iomem *dbg_snapshot_get_base_vaddr(void)
69{
70 return (void __iomem *)(dss_base.vaddr);
71}
72
73void __iomem *dbg_snapshot_get_base_paddr(void)
74{
75 return (void __iomem *)(dss_base.paddr);
76}
77
78static void dbg_snapshot_set_core_power_stat(unsigned int val, unsigned cpu)
79{
80 if (dbg_snapshot_get_enable("header"))
81 __raw_writel(val, (dbg_snapshot_get_base_vaddr() +
82 DSS_OFFSET_CORE_POWER_STAT + cpu * 4));
83}
84
85unsigned int dbg_snapshot_get_core_panic_stat(unsigned cpu)
86{
87 if (dbg_snapshot_get_enable("header"))
88 return __raw_readl(dbg_snapshot_get_base_vaddr() +
89 DSS_OFFSET_PANIC_STAT + cpu * 4);
90 else
91 return 0;
92}
93
94void dbg_snapshot_set_core_panic_stat(unsigned int val, unsigned cpu)
95{
96 if (dbg_snapshot_get_enable("header"))
97 __raw_writel(val, (dbg_snapshot_get_base_vaddr() +
98 DSS_OFFSET_PANIC_STAT + cpu * 4));
99}
100
101static void dbg_snapshot_report_reason(unsigned int val)
102{
103 if (dbg_snapshot_get_enable("header"))
104 __raw_writel(val, dbg_snapshot_get_base_vaddr() + DSS_OFFSET_EMERGENCY_REASON);
105}
106
1abf4739
HK
107int dbg_snapshot_get_debug_level_reg(void)
108{
2b577af2 109 int ret = DSS_DEBUG_LEVEL_MID;
1abf4739
HK
110
111 if (dbg_snapshot_get_enable("header")) {
112 int val = __raw_readl(dbg_snapshot_get_base_vaddr() + DSS_OFFSET_DEBUG_LEVEL);
113
114 if ((val & GENMASK(31, 16)) == DSS_DEBUG_LEVEL_PREFIX)
115 ret = val & GENMASK(15, 0);
116 }
117
118 return ret;
119}
120
dd101ca5
DC
121void dbg_snapshot_scratch_reg(unsigned int val)
122{
123 if (dbg_snapshot_get_enable("header"))
124 __raw_writel(val, dbg_snapshot_get_base_vaddr() + DSS_OFFSET_SCRATCH);
125}
126
127bool dbg_snapshot_is_scratch(void)
128{
129 return __raw_readl(dbg_snapshot_get_base_vaddr() +
130 DSS_OFFSET_SCRATCH) == DSS_SIGN_SCRATCH;
131}
132
133unsigned long dbg_snapshot_get_last_pc_paddr(void)
134{
135 /*
136 * Basically we want to save the pc value to non-cacheable region
137 * if ESS is enabled. But we should also consider cases that are not so.
138 */
139
140 if (dbg_snapshot_get_enable("header"))
141 return ((unsigned long)dbg_snapshot_get_base_paddr() + DSS_OFFSET_CORE_LAST_PC);
142 else
143 return virt_to_phys((void *)dss_desc.hardlockup_core_pc);
144}
145
146unsigned long dbg_snapshot_get_last_pc(unsigned int cpu)
147{
148 if (dbg_snapshot_get_enable("header"))
149 return __raw_readq(dbg_snapshot_get_base_vaddr() +
150 DSS_OFFSET_CORE_LAST_PC + cpu * 8);
151 else
152 return dss_desc.hardlockup_core_pc[cpu];
153}
154
155unsigned long dbg_snapshot_get_spare_vaddr(unsigned int offset)
156{
157 return (unsigned long)(dbg_snapshot_get_base_vaddr() +
158 DSS_OFFSET_SPARE_BASE + offset);
159}
160
161unsigned long dbg_snapshot_get_spare_paddr(unsigned int offset)
162{
163 unsigned long base_vaddr = 0;
164 unsigned long base_paddr = (unsigned long)dbg_snapshot_get_base_paddr();
165
166 if (base_paddr)
167 base_vaddr = (unsigned long)(base_paddr +
168 DSS_OFFSET_SPARE_BASE + offset);
169
170 return base_vaddr;
171}
172
173unsigned int dbg_snapshot_get_item_size(char* name)
174{
175 unsigned long i;
176
758cb36c 177 for (i = 0; i < dss_desc.log_cnt; i++) {
dd101ca5
DC
178 if (!strncmp(dss_items[i].name, name, strlen(name)))
179 return dss_items[i].entry.size;
180 }
181 return 0;
182}
183EXPORT_SYMBOL(dbg_snapshot_get_item_size);
184
185unsigned long dbg_snapshot_get_item_vaddr(char *name)
186{
187 unsigned long i;
188
758cb36c 189 for (i = 0; i < dss_desc.log_cnt; i++) {
dd101ca5
DC
190 if (!strncmp(dss_items[i].name, name, strlen(name)))
191 return dss_items[i].entry.vaddr;
192 }
193 return 0;
194}
195
196unsigned int dbg_snapshot_get_item_paddr(char* name)
197{
198 unsigned long i;
199
758cb36c 200 for (i = 0; i < dss_desc.log_cnt; i++) {
dd101ca5
DC
201 if (!strncmp(dss_items[i].name, name, strlen(name)))
202 return dss_items[i].entry.paddr;
203 }
204 return 0;
205}
206EXPORT_SYMBOL(dbg_snapshot_get_item_paddr);
207
208int dbg_snapshot_get_hardlockup(void)
209{
210 return dss_desc.hardlockup_detected;
211}
212EXPORT_SYMBOL(dbg_snapshot_get_hardlockup);
213
214int dbg_snapshot_set_hardlockup(int val)
215{
216 unsigned long flags;
217
218 if (unlikely(!dss_base.enabled))
219 return 0;
220
221 raw_spin_lock_irqsave(&dss_desc.ctrl_lock, flags);
222 dss_desc.hardlockup_detected = val;
223 raw_spin_unlock_irqrestore(&dss_desc.ctrl_lock, flags);
224 return 0;
225}
226EXPORT_SYMBOL(dbg_snapshot_set_hardlockup);
227
228int dbg_snapshot_early_panic(void)
229{
230 dss_soc_ops->soc_early_panic(NULL);
231 return 0;
232}
233
234int dbg_snapshot_prepare_panic(void)
235{
236 unsigned long cpu;
237
238 if (unlikely(!dss_base.enabled))
239 return 0;
240 /*
241 * kick watchdog to prevent unexpected reset during panic sequence
242 * and it prevents the hang during panic sequence by watchedog
243 */
244 dss_soc_ops->soc_start_watchdog(NULL);
245
246 dss_soc_ops->soc_prepare_panic_entry(NULL);
247
248 /* Again disable log_kevents */
249 dbg_snapshot_set_enable("log_kevents", false);
250
251 for_each_possible_cpu(cpu) {
252 if (dss_soc_ops->soc_is_power_cpu((void *)cpu))
253 dbg_snapshot_set_core_power_stat(DSS_SIGN_ALIVE, cpu);
254 else
255 dbg_snapshot_set_core_power_stat(DSS_SIGN_DEAD, cpu);
256 }
257 dss_soc_ops->soc_prepare_panic_exit(NULL);
258
259 return 0;
260}
261EXPORT_SYMBOL(dbg_snapshot_prepare_panic);
262
263int dbg_snapshot_post_panic(void)
264{
265 if (dss_base.enabled) {
dd101ca5
DC
266 dbg_snapshot_recall_hardlockup_core();
267#ifdef CONFIG_DEBUG_SNAPSHOT_PMU
268 dbg_snapshot_dump_sfr();
269#endif
270 dbg_snapshot_save_context(NULL);
5bcdb379 271
dd101ca5
DC
272 dbg_snapshot_print_panic_report();
273
6b666fd8 274 dss_soc_ops->soc_post_panic_entry(NULL);
dd101ca5
DC
275
276#ifdef CONFIG_DEBUG_SNAPSHOT_PANIC_REBOOT
277 if (!dss_desc.no_wdt_dev) {
278#ifdef CONFIG_DEBUG_SNAPSHOT_WATCHDOG_RESET
279 if (dss_desc.hardlockup_detected || num_online_cpus() > 1) {
280 /* for stall cpu */
6b666fd8 281 dbg_snapshot_spin_func();
dd101ca5
DC
282 }
283#endif
284 }
285#endif
286 }
6b666fd8
HK
287 dss_soc_ops->soc_post_panic_exit(NULL);
288
dd101ca5 289 /* for stall cpu when not enabling panic reboot */
6b666fd8 290 dbg_snapshot_spin_func();
dd101ca5
DC
291
292 /* Never run this function */
293 pr_emerg("debug-snapshot: %s DO NOT RUN this function (CPU:%d)\n",
294 __func__, raw_smp_processor_id());
295 return 0;
296}
297EXPORT_SYMBOL(dbg_snapshot_post_panic);
298
299int dbg_snapshot_dump_panic(char *str, size_t len)
300{
301 if (unlikely(!dss_base.enabled) ||
302 !dbg_snapshot_get_enable("header"))
303 return 0;
304
305 /* This function is only one which runs in panic funcion */
306 if (str && len && len < SZ_1K)
307 memcpy(dbg_snapshot_get_base_vaddr() + DSS_OFFSET_PANIC_STRING, str, len);
308
309 return 0;
310}
311EXPORT_SYMBOL(dbg_snapshot_dump_panic);
312
313int dbg_snapshot_post_reboot(char *cmd)
314{
315 int cpu;
316
317 if (unlikely(!dss_base.enabled))
318 return 0;
319
00fd6f7b
HK
320 dss_soc_ops->soc_post_reboot_entry(NULL);
321
dd101ca5
DC
322 dbg_snapshot_report_reason(DSS_SIGN_NORMAL_REBOOT);
323
324 if (!cmd)
325 dbg_snapshot_scratch_reg(DSS_SIGN_RESET);
326 else if (strcmp((char *)cmd, "bootloader") && strcmp((char *)cmd, "ramdump"))
327 dbg_snapshot_scratch_reg(DSS_SIGN_RESET);
328
329 pr_emerg("debug-snapshot: normal reboot done\n");
330
331 dbg_snapshot_save_context(NULL);
dd101ca5 332
1ffb8082
HK
333 /* clear DSS_SIGN_PANIC when normal reboot */
334 for_each_possible_cpu(cpu) {
335 dbg_snapshot_set_core_panic_stat(DSS_SIGN_RESET, cpu);
336 }
337
00fd6f7b
HK
338 dss_soc_ops->soc_post_reboot_exit(NULL);
339
dd101ca5
DC
340 return 0;
341}
342EXPORT_SYMBOL(dbg_snapshot_post_reboot);
343
344static int dbg_snapshot_reboot_handler(struct notifier_block *nb,
345 unsigned long l, void *p)
346{
347 if (unlikely(!dss_base.enabled))
348 return 0;
349
350 pr_emerg("debug-snapshot: normal reboot starting\n");
351
352 return 0;
353}
354
355static int dbg_snapshot_panic_handler(struct notifier_block *nb,
356 unsigned long l, void *buf)
357{
358 dbg_snapshot_report_reason(DSS_SIGN_PANIC);
359 if (unlikely(!dss_base.enabled))
360 return 0;
361
362#ifdef CONFIG_DEBUG_SNAPSHOT_PANIC_REBOOT
363 local_irq_disable();
364 pr_emerg("debug-snapshot: panic - reboot[%s]\n", __func__);
365#else
366 pr_emerg("debug-snapshot: panic - normal[%s]\n", __func__);
367#endif
c8241d42
YN
368 /*
369 * To get more kernel panic log
370 * dbg_snapshot_dump_task_info();
371 */
dd101ca5 372 pr_emerg("linux_banner: %s\n", linux_banner);
5bcdb379 373
dd101ca5
DC
374 return 0;
375}
376
377static struct notifier_block nb_reboot_block = {
378 .notifier_call = dbg_snapshot_reboot_handler
379};
380
381static struct notifier_block nb_panic_block = {
382 .notifier_call = dbg_snapshot_panic_handler,
383};
384
385void dbg_snapshot_panic_handler_safe(void)
386{
387 char *cpu_num[SZ_16] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
388 char text[SZ_32] = "safe panic handler at cpu ";
389 int cpu = raw_smp_processor_id();
390 size_t len;
391
392 if (unlikely(!dss_base.enabled))
393 return;
394
395 strncat(text, cpu_num[cpu], 1);
396 len = strnlen(text, SZ_32);
397
398 dbg_snapshot_report_reason(DSS_SIGN_SAFE_FAULT);
399 dbg_snapshot_dump_panic(text, len);
400 dss_soc_ops->soc_expire_watchdog((void *)NULL);
401}
402
403void dbg_snapshot_register_soc_ops(struct dbg_snapshot_helper_ops *ops)
404{
405 if (ops)
406 dss_soc_ops = ops;
407}
408
1abf4739 409void __init dbg_snapshot_init_helper(void)
dd101ca5
DC
410{
411 register_reboot_notifier(&nb_reboot_block);
412 atomic_notifier_chain_register(&panic_notifier_list, &nb_panic_block);
413 dss_soc_ops = &dss_soc_dummy_ops;
414
415 /* hardlockup_detector function should be called before secondary booting */
416 dbg_snapshot_soc_helper_init();
417}