diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index e8b1f7910490..48b75ece4c17 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -44,6 +44,8 @@ #include #include +#include + static const char *handler[]= { "Synchronous Abort", "IRQ", @@ -421,6 +423,8 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs) if (call_undef_hook(regs) == 0) return; + trace_undef_instr(regs, (void *)pc); + if (unhandled_signal(current, SIGILL) && show_unhandled_signals_ratelimited()) { pr_info("%s[%d]: undefined instruction: pc=%p\n", current->comm, task_pid_nr(current), pc); diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 7bb08670fc10..69079e5bfc84 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -40,6 +40,8 @@ #include #include +#include + static const char *fault_name(unsigned int esr); /* @@ -118,6 +120,8 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr, { struct siginfo si; + trace_user_fault(tsk, addr, esr); + if (unhandled_signal(tsk, sig) && show_unhandled_signals_ratelimited()) { pr_info("%s[%d]: unhandled %s (%d) at 0x%08lx, esr 0x%03x\n", tsk->comm, task_pid_nr(tsk), fault_name(esr), sig, diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 3228282dc49c..8c92a564299d 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -13,6 +13,14 @@ menuconfig CORESIGHT if CORESIGHT +config CORESIGHT_EVENT + tristate "CoreSight Event driver" + help + This driver provides support for registering with various events + and performing CoreSight actions like aborting trace on their + occurrence. These events can be controlled by using module + parameters. + config CORESIGHT_CSR bool "CoreSight Slave Register driver" help diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index ee3a77fede53..09433897b6a2 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_CORESIGHT) += coresight.o obj-$(CONFIG_OF) += of_coresight.o obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o +obj-$(CONFIG_CORESIGHT_EVENT) += coresight-event.o obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o diff --git a/drivers/hwtracing/coresight/coresight-event.c b/drivers/hwtracing/coresight/coresight-event.c new file mode 100644 index 000000000000..0bced010d4c5 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-event.c @@ -0,0 +1,169 @@ +/* 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) +{ + 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"); diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 10e50df1e6d5..d48d8485f979 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -1064,9 +1064,44 @@ static void tmc_disable_link(struct coresight_device *csdev, int inport, tmc_disable(drvdata, TMC_MODE_HARDWARE_FIFO); } +static void tmc_abort(struct coresight_device *csdev) +{ + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + unsigned long flags; + enum tmc_mode mode; + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) + goto out0; + + if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { + tmc_etb_disable_hw(drvdata); + } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { + if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) + tmc_etr_disable_hw(drvdata); + else if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) + __tmc_etr_disable_to_bam(drvdata); + } else { + mode = readl_relaxed(drvdata->base + TMC_MODE); + if (mode == TMC_MODE_CIRCULAR_BUFFER) + tmc_etb_disable_hw(drvdata); + else + goto out1; + } +out0: + drvdata->enable = false; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + dev_info(drvdata->dev, "TMC aborted\n"); + return; +out1: + spin_unlock_irqrestore(&drvdata->spinlock, flags); +} + static const struct coresight_ops_sink tmc_sink_ops = { .enable = tmc_enable_sink, .disable = tmc_disable_sink, + .abort = tmc_abort, }; static const struct coresight_ops_link tmc_link_ops = { diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 7214efd10db5..7baa1e750a23 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-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 @@ -103,9 +103,19 @@ static void tpiu_disable(struct coresight_device *csdev) dev_info(drvdata->dev, "TPIU disabled\n"); } +static void tpiu_abort(struct coresight_device *csdev) +{ + struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + tpiu_disable_hw(drvdata); + + dev_info(drvdata->dev, "TPIU aborted\n"); +} + static const struct coresight_ops_sink tpiu_sink_ops = { .enable = tpiu_enable, .disable = tpiu_disable, + .abort = tpiu_abort, }; static const struct coresight_ops tpiu_cs_ops = { diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index a4d2ac601556..c34599c0594d 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -383,6 +383,25 @@ out: } EXPORT_SYMBOL_GPL(coresight_disable); +void coresight_abort(void) +{ + if (!mutex_trylock(&coresight_mutex)) { + pr_err_ratelimited("coresight: abort could not be processed\n"); + return; + } + if (!curr_sink) + goto out; + + if (curr_sink->enable && sink_ops(curr_sink)->abort) { + sink_ops(curr_sink)->abort(curr_sink); + curr_sink->enable = false; + } + +out: + mutex_unlock(&coresight_mutex); +} +EXPORT_SYMBOL_GPL(coresight_abort); + static int coresight_disable_all_source(struct device *dev, void *data) { struct coresight_device *csdev; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 903a8e852f5d..66bf56640fe1 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -194,10 +194,12 @@ struct coresight_device { * Operations available for sinks * @enable: enables the sink. * @disable: disables the sink. + * @abort: captures sink trace on abort */ struct coresight_ops_sink { int (*enable)(struct coresight_device *csdev); void (*disable)(struct coresight_device *csdev); + void (*abort)(struct coresight_device *csdev); }; /** @@ -239,6 +241,7 @@ extern int coresight_enable(struct coresight_device *csdev); extern void coresight_disable(struct coresight_device *csdev); extern int coresight_timeout(void __iomem *addr, u32 offset, int position, int value); +extern void coresight_abort(void); #else static inline struct coresight_device * coresight_register(struct coresight_desc *desc) { return NULL; } @@ -248,6 +251,7 @@ coresight_enable(struct coresight_device *csdev) { return -ENOSYS; } static inline void coresight_disable(struct coresight_device *csdev) {} static inline int coresight_timeout(void __iomem *addr, u32 offset, int position, int value) { return 1; } +static inline void coresight_abort(void) {} #endif #if defined(CONFIG_OF) && defined(CONFIG_CORESIGHT) diff --git a/include/trace/events/exception.h b/include/trace/events/exception.h new file mode 100644 index 000000000000..6b525da1432e --- /dev/null +++ b/include/trace/events/exception.h @@ -0,0 +1,124 @@ +/* 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. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM exception + +#if !defined(_TRACE_EXCEPTION_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_EXCEPTION_H + +#include + +struct task_struct; + +TRACE_EVENT(user_fault, + + TP_PROTO(struct task_struct *tsk, unsigned long addr, unsigned int fsr), + + TP_ARGS(tsk, addr, fsr), + + TP_STRUCT__entry( + __string(task_name, tsk->comm) + __field(unsigned long, addr) + __field(unsigned int, fsr) + ), + + TP_fast_assign( + __assign_str(task_name, tsk->comm) + __entry->addr = addr; + __entry->fsr = fsr; + ), + + TP_printk("task_name:%s addr:%lu, fsr:%u", __get_str(task_name), + __entry->addr, __entry->fsr) +); + + +struct pt_regs; + +TRACE_EVENT(undef_instr, + + TP_PROTO(struct pt_regs *regs, void *prog_cnt), + + TP_ARGS(regs, prog_cnt), + + TP_STRUCT__entry( + __field(void *, prog_cnt) + __field(struct pt_regs *, regs) + ), + + TP_fast_assign( + __entry->regs = regs; + __entry->prog_cnt = prog_cnt; + ), + + TP_printk("pc:%p", __entry->prog_cnt) +); + +TRACE_EVENT(unhandled_abort, + + TP_PROTO(struct pt_regs *regs, unsigned long addr, unsigned int fsr), + + TP_ARGS(regs, addr, fsr), + + TP_STRUCT__entry( + __field(struct pt_regs *, regs) + __field(unsigned long, addr) + __field(unsigned int, fsr) + ), + + TP_fast_assign( + __entry->regs = regs; + __entry->addr = addr; + __entry->fsr = fsr; + ), + + TP_printk("addr:%lu, fsr:%u", __entry->addr, __entry->fsr) +); + +TRACE_EVENT(kernel_panic, + + TP_PROTO(long dummy), + + TP_ARGS(dummy), + + TP_STRUCT__entry( + __field(long, dummy) + ), + + TP_fast_assign( + __entry->dummy = dummy; + ), + + TP_printk("dummy:%ld", __entry->dummy) +); + +TRACE_EVENT(kernel_panic_late, + + TP_PROTO(long dummy), + + TP_ARGS(dummy), + + TP_STRUCT__entry( + __field(long, dummy) + ), + + TP_fast_assign( + __entry->dummy = dummy; + ), + + TP_printk("dummy:%ld", __entry->dummy) +); + +#endif + +#include diff --git a/kernel/panic.c b/kernel/panic.c index 223564d3e1f8..b4a0edc489c5 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -25,6 +25,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + #define PANIC_TIMER_STEP 100 #define PANIC_BLINK_SPD 18 @@ -80,6 +83,8 @@ void panic(const char *fmt, ...) long i, i_next = 0; int state = 0; + trace_kernel_panic(0); + /* * Disable local interrupts. This will prevent panic_smp_self_stop * from deadlocking the first cpu that invokes the panic, since @@ -181,6 +186,9 @@ void panic(const char *fmt, ...) mdelay(PANIC_TIMER_STEP); } } + + trace_kernel_panic_late(0); + if (panic_timeout != 0) { /* * This will not be a clean reboot, with everything