/* Copyright (c) 2012-2016, 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. */ #include #include #include #include #include #include #include static int event_abort_enable; static int event_abort_set(const char *val, struct kernel_param *kp); module_param_call(event_abort_enable, event_abort_set, param_get_int, &event_abort_enable, 0644); static int event_abort_early_panic = 1; static int event_abort_on_panic_set(const char *val, struct kernel_param *kp); module_param_call(event_abort_early_panic, event_abort_on_panic_set, param_get_int, &event_abort_early_panic, 0644); static void event_abort_user_fault(void *ignore, struct task_struct *task, unsigned long addr, unsigned int fsr) { coresight_abort(); pr_debug("coresight_event: task_name: %s, addr: %lu, fsr:%u", (char *)task->comm, addr, fsr); } static void event_abort_undef_instr(void *ignore, struct pt_regs *regs, void *pc) { if (user_mode(regs)) { coresight_abort(); pr_debug("coresight_event: pc: %p", pc); } } static void event_abort_unhandled_abort(void *ignore, struct pt_regs *regs, unsigned long addr, unsigned int fsr) { if (user_mode(regs)) { coresight_abort(); pr_debug("coresight_event: addr: %lu, fsr:%u", addr, fsr); } } static void event_abort_kernel_panic(void *ignore, long state) { return; } static int event_abort_register(void) { int ret; ret = register_trace_user_fault(event_abort_user_fault, NULL); if (ret) goto err_usr_fault; ret = register_trace_undef_instr(event_abort_undef_instr, NULL); if (ret) goto err_undef_instr; ret = register_trace_unhandled_abort(event_abort_unhandled_abort, NULL); if (ret) goto err_unhandled_abort; return 0; err_unhandled_abort: unregister_trace_undef_instr(event_abort_undef_instr, NULL); err_undef_instr: unregister_trace_user_fault(event_abort_user_fault, NULL); err_usr_fault: return ret; } static void event_abort_unregister(void) { unregister_trace_user_fault(event_abort_user_fault, NULL); unregister_trace_undef_instr(event_abort_undef_instr, NULL); unregister_trace_unhandled_abort(event_abort_unhandled_abort, NULL); } static int event_abort_set(const char *val, struct kernel_param *kp) { int ret; ret = param_set_int(val, kp); if (ret) { pr_err("coresight_event: error setting value %d\n", ret); return ret; } if (event_abort_enable) ret = event_abort_register(); else event_abort_unregister(); return ret; } static int event_abort_on_panic_set(const char *val, struct kernel_param *kp) { int ret; ret = param_set_int(val, kp); if (ret) { pr_err("coresight_event: error setting val on panic %d\n", ret); return ret; } if (event_abort_early_panic) { unregister_trace_kernel_panic_late(event_abort_kernel_panic, NULL); ret = register_trace_kernel_panic(event_abort_kernel_panic, NULL); if (ret) goto err; } else { unregister_trace_kernel_panic(event_abort_kernel_panic, NULL); ret = register_trace_kernel_panic_late(event_abort_kernel_panic, NULL); if (ret) goto err; } return 0; err: pr_err("coresight_event: error registering panic event %d\n", ret); return ret; } static int __init event_init(void) { int ret; ret = register_trace_kernel_panic(event_abort_kernel_panic, NULL); if (ret) { /* We do not want to fail module init. This module can still * be used to register other abort events. */ pr_err("coresight_event: error registering on panic %d\n", ret); } return 0; } module_init(event_init); static void __exit event_exit(void) { unregister_trace_kernel_panic(event_abort_kernel_panic, NULL); } module_exit(event_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Coresight Event driver to abort tracing");