PERFORCE change 96343 for review
John Birrell
jb at FreeBSD.org
Sat Apr 29 02:04:54 UTC 2006
http://perforce.freebsd.org/chv.cgi?CH=96343
Change 96343 by jb at jb_freebsd2 on 2006/04/29 02:04:08
Add a hook to trap() so that DTrace safe-loads work. This is required
so that "dtrace -n 'BEGIN{*(char *)NULL}'" doesn't cause the system
to go kaboom. (I reported this one as a milestone, but that was a
false positive. This one is real.)
The way DTrace implements safe loads is to disable interrupts and
set a 'no-fault' flag in it's per-CPU flags before executing a probe.
During the probe execution, it emulates it's DTrace Intermediate
Format (DIF) opcodes which are very like RISC ones. If it goes
to load from a memory address (like 0x0), trap() looks for the 'no-fault'
flags and records the info before offsetting the instruction pointer
in the trap frame before simply returning. This causes the offending
instruction to be skipped and execution to start from the one after.
The whole DIF opcode design assumes that with a few instructions,
processing for that opcode will be complete and DTrace will check to
per-CPU flags to see if a fault occurred. If so it goes off and
processes it's error probe. Finally the 'no-trace' flags is reset and
interrupts enabled. And life goes on.
Since the trap() code has to call a machine architecture specific
function to work out how many bytes to offset the instruction pointer
and because the dtrace device might not be loaded, I've added a hook
for DTrace to register it's instruction size function pointer. This
also avoids having to include any DTrace API info in FreeBSD's kernel.
The kernel has it's API and DTrace has to fit in with that.
* HELP #1 *
My knowledge of the trap mechanism isn't to good, so this needs a good
look at. 8-)
* HELP #2 *
While we're on the subject of DTrace accessing memory locations...
DTrace has a concept of 'toxic ranges' which are defined when the
dtrace device loads. There are ranges of memory addresses that DTrace
isn't allowed to touch. They are primarily intended to stop DTrace
accessing memory mapped devices where a simple read access might be
sufficent to create an undesirable event.
I don't know what ranges to code on i386. Can someone tell me?
Affected files ...
.. //depot/projects/dtrace/src/sys/i386/i386/trap.c#2 edit
.. //depot/projects/dtrace/src/sys/sys/cpuvar.h#2 edit
Differences ...
==== //depot/projects/dtrace/src/sys/i386/i386/trap.c#2 (text+ko) ====
@@ -49,6 +49,7 @@
#include "opt_hwpmc_hooks.h"
#include "opt_isa.h"
#include "opt_kdb.h"
+#include "opt_kdtrace.h"
#include "opt_ktrace.h"
#include "opt_npx.h"
#include "opt_trap.h"
@@ -102,6 +103,20 @@
#include <machine/clock.h>
#endif
+#ifdef KDTRACE
+#include <sys/cpuvar.h>
+
+/*
+ * This is a hook which is initialised by the dtrace module
+ * when it is loaded. This keeps the DTrace implementation
+ * opaque. All that the trap() function below needs to determine
+ * is how many instruction bytes to osset the instruction
+ * pointer before returning from a trap that occured durin a
+ * 'no-fault' DTrace probe.
+ */
+dtrace_instr_size_func_t dtrace_instr_size_func;
+#endif
+
extern void trap(struct trapframe frame);
extern void syscall(struct trapframe frame);
@@ -218,6 +233,69 @@
goto out;
#endif
+#ifdef KDTRACE
+ /*
+ * If DTrace support is compiled into the kernel, a trap can
+ * occur while DTrace executes a probe. Before executing the
+ * probe, DTrace disables interrupts and sets a flag in it's
+ * per-cpu flags to indicate that it doesn't want to fault.
+ * On returning from the the probe, the no-fault flag is
+ * cleared and finally interrupts are re-enabled.
+ *
+ * Check if DTrace has enabled 'no-fault' mode:
+ *
+ */
+ if ((cpu_core[curcpu].cpuc_dtrace_flags & CPU_DTRACE_NOFAULT) != 0) {
+ /*
+ * When the dtrace module was loaded (or initialised
+ * if linked into the kernel), it should have set it's
+ * machine dependent instruction size function pointer
+ * for use here. If not, the trap will just end up
+ * being processed as a panic like any other.
+ */
+ if (dtrace_instr_size_func != NULL) {
+ /*
+ * There are only a couple of trap types that
+ * are expected. All the rest will be handled
+ * in the usual way.
+ */
+ switch (type) {
+ /* General protection fault. */
+ case T_PROTFLT:
+ /* Flag an illegal operation. */
+ cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_ILLOP;
+
+ /*
+ * Offset the instruction pointer
+ * to the instruction following the
+ * one casing the fault.
+ */
+ goto out;
+ frame.tf_eip += (*dtrace_instr_size_func)((u_char *) frame.tf_eip);
+ /* Page fault. */
+ case T_PAGEFLT:
+ /* Flag a bad address. */
+ cpu_core[curcpu].cpuc_dtrace_flags |= CPU_DTRACE_BADADDR;
+ cpu_core[curcpu].cpuc_dtrace_illval = rcr2();
+
+ /*
+ * Offset the instruction pointer
+ * to the instruction following the
+ * one casing the fault.
+ */
+ frame.tf_eip += (*dtrace_instr_size_func)((u_char *) frame.tf_eip);
+ goto out;
+ default:
+ /*
+ * Handle all other traps in the usual
+ * way.
+ */
+ break;
+ }
+ }
+ }
+#endif
+
if ((frame.tf_eflags & PSL_I) == 0) {
/*
* Buggy application or kernel code has disabled
==== //depot/projects/dtrace/src/sys/sys/cpuvar.h#2 (text+ko) ====
@@ -55,6 +55,35 @@
#ifdef _KERNEL
extern cpu_core_t cpu_core[];
+
+/* Used by the machine dependent trap() code. */
+typedef int (*dtrace_instr_size_func_t)(u_char *);
+
+extern dtrace_instr_size_func_t dtrace_instr_size_func;
#endif /* _KERNEL */
+/*
+ * DTrace flags.
+ */
+#define CPU_DTRACE_NOFAULT 0x0001 /* Don't fault */
+#define CPU_DTRACE_DROP 0x0002 /* Drop this ECB */
+#define CPU_DTRACE_BADADDR 0x0004 /* DTrace fault: bad address */
+#define CPU_DTRACE_BADALIGN 0x0008 /* DTrace fault: bad alignment */
+#define CPU_DTRACE_DIVZERO 0x0010 /* DTrace fault: divide by zero */
+#define CPU_DTRACE_ILLOP 0x0020 /* DTrace fault: illegal operation */
+#define CPU_DTRACE_NOSCRATCH 0x0040 /* DTrace fault: out of scratch */
+#define CPU_DTRACE_KPRIV 0x0080 /* DTrace fault: bad kernel access */
+#define CPU_DTRACE_UPRIV 0x0100 /* DTrace fault: bad user access */
+#define CPU_DTRACE_TUPOFLOW 0x0200 /* DTrace fault: tuple stack overflow */
+#if defined(__sparc)
+#define CPU_DTRACE_FAKERESTORE 0x0400 /* pid provider hint to getreg */
+#endif
+#define CPU_DTRACE_ENTRY 0x0800 /* pid provider hint to ustack() */
+
+#define CPU_DTRACE_FAULT (CPU_DTRACE_BADADDR | CPU_DTRACE_BADALIGN | \
+ CPU_DTRACE_DIVZERO | CPU_DTRACE_ILLOP | \
+ CPU_DTRACE_NOSCRATCH | CPU_DTRACE_KPRIV | \
+ CPU_DTRACE_UPRIV | CPU_DTRACE_TUPOFLOW)
+#define CPU_DTRACE_ERROR (CPU_DTRACE_FAULT | CPU_DTRACE_DROP)
+
#endif /* _SYS_CPUVAR_H */
More information about the p4-projects
mailing list