171 lines
4.3 KiB
C
171 lines
4.3 KiB
C
/* Copyright (c) 2012-2014, 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 <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/init.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/tracepoint.h>
|
|
#include <linux/coresight.h>
|
|
|
|
#include <trace/events/exception.h>
|
|
|
|
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)
|
|
{
|
|
coresight_abort();
|
|
}
|
|
|
|
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");
|