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>