Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 29 Apr 2006 02:04:53 GMT
From:      John Birrell <jb@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 96343 for review
Message-ID:  <200604290204.k3T24rTP047409@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=96343

Change 96343 by jb@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 */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200604290204.k3T24rTP047409>