void *args)
{
struct die_args *dargs = args;
- dtrace_invop_hdlr_t *hdlr;
- int rval = 0;
+ struct insn insn;
- if (val == DIE_PAGE_FAULT) {
+ switch (val) {
+ case DIE_PAGE_FAULT: {
unsigned long addr = read_cr2();
- struct insn insn;
if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT))
return NOTIFY_DONE;
return NOTIFY_OK | NOTIFY_STOP_MASK;
}
- if (val == DIE_GPF) {
- struct insn insn;
-
- if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT))
- return NOTIFY_DONE;
-
- DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
-
+ case DIE_GPF: {
kernel_insn_init(&insn, (void *)dargs->regs->ip);
insn_get_length(&insn);
- dargs->regs->ip += insn.length;
+ /*
+ * It would seem that the invalid opcode generated by the LOCK
+ * prefix (0xF0) used for SDT probe points may get delivered as
+ * a general protection failure on Xen. We need to ignore them
+ * as general protection failures...
+ */
+ if (insn.length != 5 || insn.prefixes.bytes[0] != 0xf0 ||
+ insn.opcode.bytes[0] != 0x90) {
+ if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT))
+ return NOTIFY_DONE;
- return NOTIFY_DONE;
- }
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
- if (val != DIE_TRAP || dargs->trapnr != 6)
- return NOTIFY_DONE;
+ dargs->regs->ip += insn.length;
+ return NOTIFY_OK | NOTIFY_STOP_MASK;
+ }
- for (hdlr = dtrace_invop_hdlrs; hdlr != NULL; hdlr = hdlr->dtih_next) {
- if ((rval = hdlr->dtih_func(dargs->regs)) != 0)
- break;
+ /*
+ * ... and instead treat them as the SDT probe point traps that
+ * they are.
+ */
+ dargs->trapnr = 6;
}
+ case DIE_TRAP: {
+ dtrace_invop_hdlr_t *hdlr;
+ int rval = 0;
- if (rval != 0) {
- dargs->regs->ip++;
+ if (dargs->trapnr != 6)
+ return NOTIFY_DONE;
- return NOTIFY_OK | NOTIFY_STOP_MASK;
- }
+ for (hdlr = dtrace_invop_hdlrs; hdlr != NULL;
+ hdlr = hdlr->dtih_next) {
+ if ((rval = hdlr->dtih_func(dargs->regs)) != 0)
+ break;
+ }
+
+ if (rval != 0) {
+ dargs->regs->ip += insn.length;
- return NOTIFY_DONE;
+ return NOTIFY_OK | NOTIFY_STOP_MASK;
+ }
+ }
+ default:
+ return NOTIFY_DONE;
+ }
}
static struct notifier_block dtrace_die = {
.notifier_call = dtrace_die_notifier,
};
+static int dtrace_enabled = 0;
+
+void dtrace_enable(void)
+{
+ if (!dtrace_enabled)
+ register_die_notifier(&dtrace_die);
+}
+EXPORT_SYMBOL(dtrace_enable);
+
+void dtrace_disable(void)
+{
+ if (!dtrace_enabled)
+ return;
+
+ unregister_die_notifier(&dtrace_die);
+}
+EXPORT_SYMBOL(dtrace_disable);
+
void dtrace_invop_add(int (*func)(struct pt_regs *))
{
dtrace_invop_hdlr_t *hdlr;
hdlr->dtih_func = func;
hdlr->dtih_next = dtrace_invop_hdlrs;
dtrace_invop_hdlrs = hdlr;
-
- /*
- * If this is the first DTrace invalid opcode handling, register the
- * die notifier with the kernel notifier core.
- */
- if (hdlr->dtih_next == NULL)
- register_die_notifier(&dtrace_die);
}
EXPORT_SYMBOL(dtrace_invop_add);
if (prev == NULL) {
dtrace_invop_hdlrs = hdlr->dtih_next;
-
- /*
- * If there are no invalid opcode handlers left, unregister
- * from the kernel notifier core.
- */
- if (dtrace_invop_hdlrs == NULL)
- unregister_die_notifier(&dtrace_die);
} else
prev->dtih_next = hdlr->dtih_next;