diff -urN 2.4.0/Documentation/00-INDEX 2.4.0-ikd-1/Documentation/00-INDEX --- 2.4.0/Documentation/00-INDEX Tue Nov 28 18:39:59 2000 +++ 2.4.0-ikd-1/Documentation/00-INDEX Mon Jan 15 18:40:04 2001 @@ -35,6 +35,8 @@ - info on Computone Intelliport II/Plus Multiport Serial Driver cpqarray.txt - info on using Compaq's SMART2 Intelligent Disk Array Controllers. +debugging.txt + - summary of the integrated kernel debugging patch devices.tex - LaTeX source listing of all the nodes in /dev/ with major minor #'s devices.txt @@ -83,6 +85,8 @@ - summary listing of command line / boot prompt args for the kernel. kmod.txt - info on the kernel module loader/unloader (kerneld replacement). +ktrace.txt + - tracing kernel procedure flow and timing. locks.txt - info on file locking implementations, flock() vs. fcntl(), etc. logo.gif diff -urN 2.4.0/Documentation/Configure.help 2.4.0-ikd-1/Documentation/Configure.help --- 2.4.0/Documentation/Configure.help Fri Jan 5 02:19:26 2001 +++ 2.4.0-ikd-1/Documentation/Configure.help Mon Jan 15 18:40:04 2001 @@ -130,6 +130,26 @@ If you have system with several CPU's, you do not need to say Y here: APIC will be used automatically. +Default NMI Watchdog Active on Uniprocessors +CONFIG_UP_NMI_WATCHDOG + The NMI (Non Maskable Interrupt) watchdog detects if a cpu is + spinning with normal interrupts disabled. To the outside world the + machine appears to be hung and you cannot use the keyboard to get its + attention. The NMI watchdog forcibly interrupts after approximately + 5 seconds, dropping the machine into a suitable error message which + helps to diagnose the problem. + + On SMP the NMI watchdog is generated using the IO-APIC which is + always available, so on SMP the NMI watchdog defaults to on. On UP + the NMI watchdog requires the use of MSR performance counter 1 which + conflicts with people doing performance monitoring. On UP the + default is no NMI watchdog. + + If you want the NMI watchdog to default to on for uniprocessors, say + Y here. Even if you say N here, you can boot with the NMI watchdog + active and you can activate or deactivate it on the fly, see + Documentation/nmi_watchdog.txt. + Kernel math emulation CONFIG_MATH_EMULATION Linux can emulate a math coprocessor (used for floating point @@ -14569,6 +14589,189 @@ send a BREAK and then within 5 seconds a command keypress. The keys are documented in Documentation/sysrq.txt. Don't say Y unless you really know what this hack does. + +Kernel debugging support +CONFIG_KERNEL_DEBUGGING + Shows low level kernel tracing, debugging and general hacking tools. + Mere mortals say N. + +Debug kernel stack overflows +CONFIG_DEBUG_KSTACK + If you see "kernel stack corruption. Aiee" messages, and a kernel + hacker told you to 'switch on kernel stack debugging', then this + is the right option =B-) + Do 'make clean' after changing this option! + For normal systems, this option adds noticeable overhead, so say N. + +Kernel Stack Meter +CONFIG_KSTACK_METER + With this option set, the kernel will log the minimum stack left + following boot of the machine, and the function that was executing + when the machine reached that value. This is useful for determining + how much stack the kernel needs. It also allow us to detect if there + is some piece of code that could be optimized to run without eating + a lot of stack. To see the current values: + `cat /proc/sys/debug/kstack-meter' + To reinitialize the counter to default: + `echo -1 0 >/proc/sys/debug/kstack-meter' + The first integer is the minimum stack size left. The second is the + function that was running when that condition was reached. + For normal systems, this option adds noticeable overhead, so say N. + + With this option enabled the IA32 NMI watchdog will be disabled. + +Kernel stack overflow threshold +CONFIG_KSTACK_THRESHOLD + If the stack has less bytes than this left, assume you are headed for an + overflow. + +Detect software lockups +CONFIG_DEBUG_SOFTLOCKUP + If you see strange lockups and a kernel hacker told you to 'switch + on software lockup detection', then this is the right option =B-) + Do 'make clean' after changing this option! + For normal systems, this option adds noticeable overhead, so say N. + +Deadlock threshold +CONFIG_SOFTLOCKUP_THRESHOLD + The number of procedure calls a process can make without going + through schedule. Any process that does more calls than this number + is "looping". Alas it does not catch inline procedure calls. + +Enable kernel tracer +CONFIG_TRACE + For kernel hackers who want to know where the path of execution goes + and how much time the kernel spends in the various procedures. The + trace is stored in /proc/trace (say Y to "/proc filesystem support"!) + and in order to read it, you need the ktrace program, see + scripts/ktrace. For normal systems, this option adds noticeable + overhead, so say N. + + With this option enabled the IA32 NMI watchdog will be disabled. + +Size of trace buffer +CONFIG_TRACE_SIZE + The number of trace entries to store in the kernel. + +Trace timestamp +CONFIG_TRACE_TIMESTAMP + Attempts to store an accurate timestamp against each trace entry, + scripts/ktrace will calculate the interval between successive + entries. On processors where an accurate timestamp is not available, + the jiffie counter is used instead. Jiffies are almost useless + because most procedure calls run in less than one jiffie but it is + better than nothing. Recommended if you want procedure times and your + cpu supports an accurate timestamp, however it adds 64 or 32 bits to + each trace entry. + +Truncated trace timestamp +CONFIG_TRACE_TRUNCTIME + If the full timestamp field is taking up too much room (64 bits per + entry on x86) and you are willing to risk wraparound of the + timestamp, say Y here. Only the last 32 bits of the timestamp will + be stored. Unless you are *really* short on storage, say N. + +Process ID for trace +CONFIG_TRACE_PID + If you want to know which process a trace table entry is for, say Y + here. Recommended but adds sizeof(pid_t) to each trace table entry. + +Cpu ID for tracer +CONFIG_TRACE_CPU + If you want to know which cpu a trace table entry is for, say Y here. + Only effective on SMP systems. Recommended but it adds sizeof(int) + to each trace table entry. + +Emergency trace length +CONFIG_ETRACE_LENGTH + This option controls the number of trace table entries printed in the + event of a kernel Oops. The default value (30) is usually enough. + +Memleak, Kernel memory leak detection support +CONFIG_MEMLEAK + For kernel hackers who want to track down memory leaks in the + kernel, say Y here and look at scripts/memleak. Mere mortals say N. + +GCC profiling support +CONFIG_PROFILE_GCC + This option improves the kernel profiling by using the gcc profiling feature. + With this option enabled the kernel will use gcc profiling, not once + each timer interrupt. This option enabled will add a lot of overhead to + the kernel. If you want to run this kernel for production and you want + profiling, it's recommended that you use normal profiling and that you + say N here. + +Print %eip to resolve symbols from locks +CONFIG_PRINT_EIP + This allows the kernel to print on the console the %eip address every + time a kernel function is called. This facilitates the resolution + of addresses after a complete machine lockup. A system with this + enabled should only be run in console mode, not X. The %eip addresses + are only displayed on virtual consoles (/dev/tty0...), and not + on serial consoles. This displays a column for each CPU and a one-up + counter for each CPU. If your system locks up while this feature is + enabled, for each CPU record the first column (the address) sorted + by the second column (the one-up counter). ksymoops or a manual + trace of Symbols.map may then be used to determine the lockup. + For normal systems, this option adds noticeable overhead, so say N. + Say Y here if a kernel hacker tell you to do that. + +Get Free Pages poisoner +CONFIG_GFP_POISON + Enable this option to make memory corruption at the GFP layer a bit + more visible. + +SLAB poisoner +CONFIG_SLAB_POISON + Enable this option to make memory corruption at the SLAB layer a bit + more visible. + +Semapahore deadlock detector +CONFIG_SEMAPHORE_DEADLOCK + With this option enabled, the first down() that will block for more than + 20 sec will generate an Oops that will allow you to know the code path + that deadlocked. + +Kernel Debugging support +CONFIG_KDB + This option provides a built-in kernel debugger. The built-in + kernel debugger contains commands which allow memory to be examined, + instructions to be disassembled and breakpoints to be set. For details, + see Documentation/kdb/kdb.mm and the manual pages kdb_bt, kdb_ss, etc. + Kdb can also be used via the serial port. Set up the system to + have a serial console (see Documentation/serial-console.txt). + The Control-A key sequence on the serial port will cause the + kernel debugger to be entered with input from the serial port and + output to the serial console. Selecting this option will + automatically set CONFIG_KALLSYMS. If unsure, say N. + +KDB off by default +CONFIG_KDB_OFF + Normally kdb is activated by default, as long as CONFIG_KDB is set. + If you want to ship a kernel with kdb support but only have kdb + turned on when the user requests it then select this option. When + compiled with CONFIG_KDB_OFF, kdb ignores all events unless you boot + with kdb=on or you echo "1" > /proc/sys/kernel/kdb. This option also + works in reverse, if kdb is normally activated, you can boot with + kdb=off or echo "0" > /proc/sys/kernel/kdb to deactivate kdb. If + unsure, say N. + +Load all symbols for debugging +CONFIG_KALLSYMS + Normally only exported symbols are available to modules. For + debugging you may want all symbols, not just the exported ones. If + you say Y here then extra data is added to the kernel and modules, + this data lists all the non-stack symbols in the kernel or module + and can be used by any debugger. You need modutils >= 2.3.11 to use + this option. See "man kallsyms" for the data format, it adds 10-20% + to the size of the kernel and the loaded modules. If unsure, say N. + +Kernel lock metering +CONFIG_LOCKMETER + Say Y to enable kernel lock metering, which adds overhead to SMP locks, + but allows you to see various statistics using the lockstat command. + + If you don't know what to do here, say N. ISDN subsystem CONFIG_ISDN diff -urN 2.4.0/Documentation/debugging.txt 2.4.0-ikd-1/Documentation/debugging.txt --- 2.4.0/Documentation/debugging.txt Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/Documentation/debugging.txt Mon Jan 15 18:40:04 2001 @@ -0,0 +1,80 @@ +Debugging the kernel for fun and profit. + +Assorted tools of varying usefulness exist to debug the kernel. By far +the best debugging tool is the human brain. As Linus has said :- + + ... + I'm afraid that I've seen too many people fix bugs + by looking at debugger output, and that almost + inevitably leads to fixing the symptoms rather than + the underlying problems. + ... + "Use the Source, Luke, use the Source. Be one with + the code.". Think of Luke Skywalker discarding the + automatic firing system when closing on the deathstar, + and firing the proton torpedo (or whatever) manually. + _Then_ do you have the right mindset for fixing kernel + bugs. + ... + +Having said that, sometimes reading the source is not enough. The +following tools exist in the IKD patch :- + + Debug kernel stack overflows + Detect software lockups + Kernel tracer (show logic flow through procedures) + + Written by Ingo Molnar . Currently + maintained by Mike Galbraith . + + Print-EIP on video ram + + Improved by Andrea Arcangeli. + + Kernel stack meter + Kernel real profiling + Semaphore deadlock detector + + Developed by Andrea Arcangeli. + + kdb + Written by Scott Lurndal (SGI) + Integration into IKD by Andrea Arcangeli (v1.0) and Keith Owens + (v1.1 with kallsyms). + + free_pages poisoner + Written by Andrea Arcangeli + + slab posioner made a config option + Done by Andrea Arcangeli + + CONFIG_KALLSYMS added, makes all non-stack kernel symbols available + to debuggers. Needs `kallsyms` from modutils >= 2.3.11. + Written by Keith Owens + + lockmeter + Written by John Hawkes (SGI) + +COMPILER NOTE: all the features that needs the profiling stuff + (like the kernel tracer) needs a recent compiler + (gcc-2.7.2.3 doesn't work anymore with them). + I think the problem is that old good gcc doesn't like + the init sections. The suggested compiler at 19991219 + is egcs-2.91.66. + +The original merge of debugging tools into a single patch set (IKD) +is been done by Keith Owens . +PGP 2.6 917/C817FEC9. +Fingerprint 2B 25 0A 31 02 AE CA F7 73 0C 28 69 4A 7B 65 27 +PGP 5/GPG +pub 1024D/27B464EA 1998-05-27 Keith Owens + Key fingerprint = A8AD 7F99 34E6 546C 8D00 5375 8B85 0737 27B4 64EA +uid Keith Owens +sub 2048g/44ABB66C 1998-05-27 + +Currently the IKD patch is maintained by Andrea Arcangeli +and Mike Galbraith and is dowloadable at: + + ftp://ftp.*.kernel.org/pub/linux/kernel/people/andrea/ikd/ + +Have fun with it. diff -urN 2.4.0/Documentation/kdb/kdb.mm 2.4.0-ikd-1/Documentation/kdb/kdb.mm --- 2.4.0/Documentation/kdb/kdb.mm Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/Documentation/kdb/kdb.mm Mon Jan 15 18:40:04 2001 @@ -0,0 +1,284 @@ +.TH KDB 8 "02 October 2000" +.hy 0 +.SH NAME +Built-in Kernel Debugger for Linux - v1.5 +.SH "Overview" +This document describes the built-in kernel debugger available +for linux. This debugger allows the programmer to interactively +examine kernel memory, disassemble kernel functions, set breakpoints +in the kernel code and display and modify register contents. +.P +A symbol table is included in the kernel image and in modules which +enables all non-stack symbols (including static symbols) to be used as +arguments to the kernel debugger commands. +.SH "Getting Started" +To include the kernel debugger in a linux kernel, use a +configuration mechanism (e.g. xconfig, menuconfig, et. al.) +to enable the \fBCONFIG_KDB\fP option. Additionally, for accurate +stack tracebacks, it is recommended that the \fBCONFIG_FRAME_POINTER\fP +option be enabled. \fBCONFIG_FRAME_POINTER\fP changes the compiler +flags so that the frame pointer register will be used as a frame +pointer rather than a general purpose register. +.P +After linux has been configured to include the kernel debugger, +make a new kernel with the new configuration file (a make clean +is recommended before making the kernel), and install the kernel +as normal. +.P +You can compile a kernel with kdb support but have kdb off by default, +select \fBCONFIG_KDB_OFF\fR. Then the user has to explicitly activate +kdb by booting with the 'kdb=on' flag or, after /proc is mounted, by +.nf + echo "1" > /proc/sys/kernel/kdb +.fi +You can also do the reverse, compile a kernel with kdb on and +deactivate kdb with the boot flag 'kdb=off' or, after /proc is mounted, +by +.nf + echo "0" > /proc/sys/kernel/kdb +.fi +.P +When booting the new kernel using \fIlilo\fP(1), the 'kdb=early' flag +may be added after the image name on the \fBLILO\fP boot line to +force the kernel to stop in the kernel debugger early in the +kernel initialization process. 'kdb=early' implies 'kdb=on'. +If the 'kdb=early' flag isn't provided, then kdb will automatically be +invoked upon system panic or when the \fBPAUSE\fP key is used from the +keyboard, assuming that kdb is on. Older versions of kdb used just a +boot flag of 'kdb' to activate kdb early, this is still supported but +is deprecated. +.P +Kdb can also be used via the serial port. Set up the system to +have a serial console (see \fIDocumentation/serial-console.txt\fP). +The \fBControl-A\fP key sequence on the serial port will cause the +kernel debugger to be entered, assuming that kdb is on. +.P +If you have both a keyboard+video and a serial console, you can use +either for kdb. +Define both video and serial consoles with boot parameters +.P +.nf + console=tty0 console=/dev/ttyS0,38400 +.fi +.P +Any kdb data entered on the keyboard or the serial console will be echoed +to both. +.P +While kdb is active, the keyboard (not serial console) indicators will strobe. +The caps lock and scroll lock lights will turn on and off, num lock is not used +because it can confuse laptop keyboards where the numeric keypad is mapped over +the normal keys. +On exit from kdb the keyboard indicators will probably be wrong, they will not match the kernel state. +Pressing caps lock twice should get the indicators back in sync with +the kernel. +.SH "Basic Commands" +There are several categories of commands available to the +kernel debugger user including commands providing memory +display and modification, register display and modification, +instruction disassemble, breakpoints and stack tracebacks. +.P +The following table shows the currently implemented commands: +.DS +.TS +box, center; +l | l +l | l. +Command Description +_ +bc Clear Breakpoint +bd Disable Breakpoint +be Enable Breakpoint +bl Display breakpoints +bp Set or Display breakpoint +bph Set or Display hardware breakpoint +bpa Set or Display breakpoint globally +bpha Set or Display hardware breakpoint globally +bt Stack backtrace for current process +btp Stack backtrace for specific process +bta Stack backtrace for all processes +cpu Display or switch cpus +ef Print exception frame +env Show environment +go Restart execution +help Display help message +id Disassemble Instructions +ll Follow Linked Lists +lsmod List loaded modules +md Display memory contents +mdr Display raw memory contents +mds Display memory contents symbolically +mm Modify memory contents +reboot Reboot the machine +rd Display register contents +rm Modify register contents +rmmod Remove a module +sections List information on all known sections +set Add/change environment variable +sr Invoke SysReq commands +ss Single step a cpu +ssb Single step a cpu until a branch instruction +.TE +.DE +.P +Some commands can be abbreviated, such commands are indicated by a +non-zero \fIminlen\fP parameter to \fBkdb_register\fP; the value of +\fIminlen\fP being the minimum length to which the command can be +abbreviated (for example, the \fBgo\fP command can be abbreviated +legally to \fBg\fP). +.P +If an input string does not match a command in the command table, +it is treated as an address expression and the corresponding address +value and nearest symbol are shown. +.P +Some of the commands are described here. +Information on the more complicated commands can be found in the +appropriate manual pages. +.TP 8 +cpu +With no parameters, it lists the available cpus, '*' after a cpu number +indicates a cpu that did not respond to the kdb stop signal. +.I cpu +followed by a number will switch to that cpu, you cannot switch to +a cpu marked '*'. +This command is only available if the kernel was configured for SMP. +.TP 8 +go +Continue normal execution. +Active breakpoints are reestablished and the processor(s) allowed to +run normally. +To continue at a specific address, use +.I rm +to change the instruction pointer then go. +.TP 8 +id +Disassemble instructions starting at an address. +Environment variable IDCOUNT controls how many lines of disassembly +output the command produces. +.TP 8 +lsmod +Internal command to list modules. +This does not use any kernel nor user space services so can be used at any time. +.TP 8 +reboot +Reboot the system, with no attempt to do a clean close down. +.TP 8 +rmmod +Internal command to remove a module. +This does not use any user space services, however it calls the module +cleanup routine and that routine may try to use kernel services. +Because kdb runs disabled there is no guarantee that the module cleanup +routine will succeed, there is a real risk of the routine hanging and +taking kdb with it. +Use the +.I rmmod +command with extreme care. +.TP 8 +sections +List information for all known sections. The output is one line per +module plus the kernel, starting with the module name. This is +followed by one or more repeats of section name, section start, +section end and section flags. This data is not designed for human +readability, it is intended to tell external debuggers where each +section has been loaded. +.TP 8 +sr +Invoke the SysReq code. +This command takes a single character which is passed to SysReq +processing, as if you had entered the SysReq key sequence followed by +that character. +.SH INITIAL KDB COMMANDS +kdb/kdb_cmds is a plain text file where you can define kdb commands +which are to be issued during kdb_init(). One command per line, blank +lines are ignored, lines starting with '#' are ignored. kdb_cmds is +intended for per user customization of kdb, you can use it to set +environment variables to suit your hardware or to set standard +breakpoints for the problem you are debugging. This file is converted +to a small C object, compiled and linked into the kernel. You must +rebuild and reinstall the kernel after changing kdb_cmds. This file +will never be shipped with any useful data so you can always override +it with your local copy. Sample kdb_cmds: +.P +.nf +# Initial commands for kdb, alter to suit your needs. +# These commands are executed in kdb_init() context, no SMP, no +# processes. Commands that require process data (including stack or +# registers) are not reliable this early. set and bp commands should +# be safe. Global breakpoint commands affect each cpu as it is booted. + +set LINES=50 +set MDCOUNT=25 +set RECURSE=1 +bp sys_init_module +.fi +.SH INTERRUPTS AND KDB +When a kdb event occurs, one cpu (the initial cpu) enters kdb state. +It uses a cross system non maskable interrupt (NMI) to interrupt the +other cpus and bring them all into kdb state. All cpus run with +interrupts disabled while they are inside kdb, this prevents most +external events from disturbing the kernel while kdb is running. +.B Note: +Disabled interrupts means that any I/O that relies on interrupts cannot +proceed while kdb is in control, devices can time out. The clock tick +is also disabled, machines will lose track of time while they are +inside kdb. +.P +Even with interrupts disabled, some NMI events will still occur, these +can disturb the kernel while you are debugging it. The initial cpu +will still accept NMI events, assuming that kdb was not entered for an +NMI event. Any cpu where you use the SS or SSB commands will accept +NMI events, even after the instruction has finished and the cpu is back +in kdb. This is an unavoidable side effect of the fact that doing +SS[B] requires the cpu to drop all the way out of kdb, including +exiting from the NMI event that brought the cpu into kdb. Under normal +circumstances the only NMI event is for the NMI oopser and that is kdb +aware so it does not disturb the kernel while kdb is running. +.P +Sometimes doing SS or SSB on ix86 will allow one interrupt to proceed, +even though the cpu is disabled for interrupts. I have not been able +to track this one down but I suspect that the interrupt was pending +when kdb was entered and it runs when kdb exits through IRET even +though the popped flags are marked as cli(). If any ix86 hardware +expert can shed some light on this problem, please notify the kdb +maintainer. +.SH RECOVERING FROM KDB ERRORS +If a kdb command breaks and kdb has enough of a recovery environment +then kdb will abort the command and drop back into mainline kdb code. +This means that user written kdb commands can follow bad pointers +without killing kdb. Ideally all code should verify that data areas +are valid (using kdba_getword) before accessing it but lots of calls +to kdba_getword can be clumsy. +.SH DEBUGGING THE DEBUGGER +kdb has limited support for debugging problems within kdb. If you +suspect that kdb is failing, you can set environment variable KDBDEBUG +to a bit pattern which will activate kdb_printf statements within kdb. +See include/linux/kdb.h, KDB_DEBUG_FLAG_xxx defines. For example +.nf + set KDBDEBUG=0x60 +.fi +activates the event callbacks into kdb plus state tracing in sections +of kdb. +.nf + set KDBDEBUG=0x18 +.fi +gives lots of tracing as kdb tries to decode the process stack. +.P +You can also perform one level of recursion in kdb. If environment +variable RECURSE is not set or is 0 then kdb will either recover from +an error (if the recovery environment is satisfactory) or kdb will +allow the error to percolate, usually resulting in a dead system. When +RECURSE is 1 then kdb will recover from an error or, if there is no +satisfactory recovery environment, it will drop into kdb state to let +you diagnose the problem. When RECURSE is 2 then all errors drop into +kdb state, kdb does not attempt recovery first. Errors while in +recursive state all drop through, kdb does not even attempt to recover +from recursive errors. +.SH WRITING NEW COMMANDS +TBD +.SH AUTHORS +Scott Lurndal, Richard Bass, Scott Foehner, Srinivasa Thirumalachar, +Masahiro Adegawa, Marc Esipovich, Ted Kline, Steve Lord, Andi Kleen. +.br +Keith Owens - kdb maintainer. +.SH SEE ALSO +.P +linux/Documentation/kdb/kdb_{bp,bt,env,ll,md,rd,ss}.man diff -urN 2.4.0/Documentation/kdb/kdb_bp.man 2.4.0-ikd-1/Documentation/kdb/kdb_bp.man --- 2.4.0/Documentation/kdb/kdb_bp.man Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/Documentation/kdb/kdb_bp.man Mon Jan 15 18:40:04 2001 @@ -0,0 +1,172 @@ +.TH BD 1 "19 May 2000" +.SH NAME +bp, bpa, bph, bpha, bd, bc, be, bl \- breakpoint commands +.SH SYNOPSIS +bp \fIaddress-expression\fP +.LP +bpa \fIaddress-expression\fP +.LP +bph \fIaddress-expression\fP [\f(CWDATAR|DATAW|IO\fP [\fIlength\fP]] +.LP +bpha \fIaddress-expression\fP [\f(CWDATAR|DATAW|IO\fP [\fIlength\fP]] +.LP +bd \fIbreakpoint-number\fP +.LP +bc \fIbreakpoint-number\fP +.LP +be \fIbreakpoint-number\fP +.LP +bl +.SH DESCRIPTION +.hy 0 +The +.B bp +family of commands are used to establish a breakpoint. +The \fIaddress-expression\fP may be a numeric value (decimal or +hexidecimal), a symbol name, a register name preceeded by a +percent symbol '%', or a simple expression consisting of a +symbol name, an addition or subtraction character and a numeric +value (decimal or hexidecimal). +.P +\fBbph\fP and \fBbpha\fP will force the use of a hardware register, provided +the processor architecture supports them. +.P +The \fIaddress-expression\fP may also consist of a single +asterisk '*' symbol which indicates that the command should +operate on all existing breakpoints (valid only for \fBbc\fP, +\fBbd\fP and \fBbe\fP). +.P +Four different types of +breakpoints may be set: + +.TP 8 +Instruction +Causes the kernel debugger to be invoked from the debug exception +path when an instruction is fetched from the specified address. This +is the default if no other type of breakpoint is requested or when +the \fBbp\fP command is used. + +.TP 8 +DATAR +Causes the kernel debugger to be entered when data of length +\fIlength\fP is read from or written to the specified address. +This type of breakpoint must use a processor debug register which +places an architecture dependent limit on the number of data and I/O +breakpoints that may be established. +The \fBbph\fP or \fBbpha\fP commands must be used. + +.TP 8 +DATAW +Enters the kernel debugger when data of length \fIlength\fP +is written to the specified address. \fIlength\fP defaults +to four bytes if it is not explicitly specified. +Note that the processor may have already overwritten the prior data at +the breakpoint location before the kernel debugger is invoked. +The prior data should be saved before establishing the breakpoint, if +required. +The \fBbph\fP or \fBbpha\fP commands must be used. + +.TP 8 +IO +Enters the kernel debugger when an \fBin\fP or \fBout\fP instruction +targets the specified I/O address. The \fBbph\fP or \fBbpha\fP +commands must be used. + +.P +The +.B bpha +command will establish a breakpoint on all processors in an +SMP system. This command is not available in an uniprocessor +kernel. +.P +The +.B bd +command will disable a breakpoint without removing it from the kernel +debugger's breakpoint table. +This can be used to keep breakpoints in the table without exceeding the +architecture limit on breakpoint registers. +.P +The +.B be +command will re-enable a disabled breakpoint. +.P +The +.B bc +command will clear a breakpoint from the breakpoint table. +.P +The +.B bl +command will list the existing set of breakpoints. +.SH LIMITATIONS +There is a compile time limit of sixteen entries in the +breakpoint table at any one time. +.P +There are architecture dependent limits on the number of hardware +breakpoints that can be set. +.IP ix86 8 +Four. +.PD 0 +.IP ia64 8 +? +.PD 1 +.SH ENVIRONMENT +The breakpoint subsystem does not currently use any environment +variables. +.SH SMP CONSIDERATIONS +Using +.B bc +is risky on SMP systems. +If you clear a breakpoint when another cpu has hit that breakpoint but +has not been processed then it may not be recognised as a kdb +breakpoint, usually resulting in incorrect program counters and kernel +panics. +It is safer to disable the breakpoint with +.BR bd , +then +.B go +to let any other processors that are waiting on the breakpoint to +clear. +After all processors are clear of the disabled breakpoint then it is +safe to clear it using +.BR bc . +.P +Breakpoints which use the processor breakpoint registers +are only established on the processor which is +currently active. If you wish breakpoints to be universal +use the +.B bpa +or +.B bpha +commands. +.SH EXAMPLES +.TP 8 +bp schedule +Sets an instruction breakpoint at the begining of the +function \fBschedule\fP. + +.TP 8 +bp schedule+0x12e +Sets an instruction breakpoint at the instruction located +at \fBschedule\fP+\fI0x12e\fP. + +.TP 8 +bph ttybuffer+0x24 dataw +Sets a data write breakpoint at the location referenced by +\fBttybuffer\fP+\fI0x24\fP for a length of four bytes. + +.TP 8 +bph 0xc0254010 datar 1 +Establishes a data reference breakpoint at address \fB0xc0254010\fP +for a length of one byte. + +.TP 8 +bp +List current breakpoint table. + +.TP 8 +bd 0 +Disable breakpoint #0. + +.TP 8 +bc * +Clear all breakpoints diff -urN 2.4.0/Documentation/kdb/kdb_bt.man 2.4.0-ikd-1/Documentation/kdb/kdb_bt.man --- 2.4.0/Documentation/kdb/kdb_bt.man Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/Documentation/kdb/kdb_bt.man Mon Jan 15 18:40:04 2001 @@ -0,0 +1,172 @@ +.TH BT 1 "16 September 2000" +.SH NAME +bt \- Stack Traceback command +.SH SYNOPSIS +bt [ ] +.LP +btp +.LP +bta +.SH DESCRIPTION +.hy 0 +The +.B bt +command is used to print a stack traceback. It uses the +current registers (see \fBrd\fP command) to determine +the starting context and attempts to provide a complete +stack traceback for the active thread. If \fIstack-frame-address\fP +is supplied, it is assumed to point to the start of a valid +stack frame and the stack will be traced back from that +point (e.g. on i386 architecture, \fIstack-frame-address\fP +should be the stack address of a saved \fB%eip\fP value from a \fBcall\fP +instruction). +.P +A kernel configuration option \fBCONFIG_FRAME_POINTER\fP should +be enabled so that the compiler will utilize the frame pointer +register properly to maintain a stack which can be correctly +analyzed. +.P +The \fBbt\fP command will attempt to analyze the stack without +frame pointers if the \fBCONFIG_FRAME_POINTER\fP option is not +enabled, but the analysis is difficult and may not produce +accurate nor complete results. +.P +The \fBbtp\fP command will analyze the stack for the given +process identification (see the \fBps\fP command). +.P +The \fBbta\fP command lists the stack for all processes. +.P +For each function, the stack trace prints at least two lines. +The first line contains four or five fields\ :- +.IP * 3 +The pointer to the previous stack frame, blank if there is no valid +frame pointer. +.PD 0 +.IP * 3 +The current address within this frame. +.IP * 3 +The address converted to a function name (actually the first non-local +label which is <= the address). +.IP * 3 +The offset of the address within the function. +.IP * 3 +Any parameters to the function. +.PD 1 +.PP +On the next line there are five fields which are designed to make it +easier to match the trace against the kernel code\ :- +.IP * 3 +The module name that contains the address, "kernel" if it is in the +base kernel. +.PD 0 +.IP * 3 +The section name that contains the address. +.IP * 3 +The start address of the section. +.IP * 3 +The start address of the function. +.IP * 3 +The end address of the function (the first non-local label which is > +the address). +.PD 1 +.PP +If arguments are being converted to symbols, any argument which +converts to a kernel or module address is printed as\ :- +.IP * 3 +Argument address. +.PD 0 +.IP * 3 +The module name that contains the address, "kernel" if it is in the +base kernel. +.IP * 3 +The symbol name the argument maps to. +.IP * 3 +The offset of the argument from the symbol, suppressed if 0. +.PD 1 +.SH MATCHING TRACE TO KERNEL CODE +The command "objdump\ -S" will disassemble an object and, if the code +was compiled with debugging (gcc flag -g), objdump will interleave the +C source lines with the generated object. +.PP +A complete objdump of the kernel or a module is too big, normally you +only want specific functions. +By default objdump will only print the .text section but Linux uses +other section names for executable code. +When objdump prints relocatable objects (modules) it uses an offset of +0 which is awkward to relate to the stack trace. +The five fields which are printed for each function are designed to +make it easier to match the stack trace against the kernel code using +"objdump\ -S". +.PP +If the function is in the kernel then you need the section name, the +start and end address of the function. The command is +.PP +.nf + objdump -S -j \\ + --start-address= \\ + --stop-address= \\ + /usr/src/linux/vmlinux +.fi +.PP +If the function is in a module then you need the section name, the +start address of the section, the start and end address of the +function, the module name. The command is +.PP +.nf + objdump -S -j \\ + --adjust-vma= \\ + --start-address= \\ + --stop-address= \\ + /path/to/module/.o +.fi +.PP +All addresses to objdump must be preceded by '0x' if they are in hex, +objdump does not assume hex. +The stack trace values are printed with leading '0x' to make it easy to +run objdump. +.SH LIMITATIONS +If the kernel is compiled without frame pointers, stack tracebacks +may be incomplete. The \fBmds %esp\fP command may be useful in +attemping to determine the actual stack traceback manually. +.P +A stack trace can be misleading if any code in a function exit has been +executed, the stack is partially unwound at that stage. +.P +The \fBbt\fP command may print more arguments for a function +than that function accepts; this happens when the C compiler +doesn't immediately pop the arguments off the stack upon return +from a called function. When this is this case, these extra +stack words will be considered additional arguments by the \fBbt\fP +command. +.SH ENVIRONMENT +The \fBBTARGS\fP environment variable governs the maximum number +of arguments that are printed for any single function. +.PP +If the \fBBTSYMARG\fP environment variable is non-zero then any +arguments that fall within the kernel are converted to symbols. +.PP +If the \fBNOSECT\fP environment variable is non-zero then the +section information is suppressed. +.PP +The \fBBTAPROMPT\fP environment variable controls the prompt after each +process is listed by the \fBbta\fP command. If \fBBTAPROMPT\fP is not +set or is non-zero then \fBbta\fP issues a prompt after each process is +listed. If \fBBTAPROMPT\fP is set to zero then no prompt is issued and +all processes are listed without human intervention. +.SH SMP CONSIDERATIONS +None. +.SH EXAMPLES +.nf +.na +.ft CW +Entering kdb (0xc3cb4000) due to Breakpoint @ 0xc011725d +Instruction(i) breakpoint #0 at 0xc011725c +qm_modules+0xd1: movl %ebp,%esp +kdb> bt + EBP EIP Function(args) +0xc3cb5f98 0xc011725d qm_modules+0xd1 (0x80721c0, 0x100, 0xbfff5000) + kernel .text 0xc0100000 0xc011718c 0xc0117264 +0xc3cb5fbc 0xc0117875 sys_query_module+0x1b1 (0x0, 0x1, 0x80721c0, 0x100, 0xbfff5000) + kernel .text 0xc0100000 0xc01176c4 0xc01178e8 + 0xc01095f8 system_call+0x34 + kernel .text 0xc0100000 0xc01095c4 0xc01095fc diff -urN 2.4.0/Documentation/kdb/kdb_env.man 2.4.0-ikd-1/Documentation/kdb/kdb_env.man --- 2.4.0/Documentation/kdb/kdb_env.man Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/Documentation/kdb/kdb_env.man Mon Jan 15 18:40:04 2001 @@ -0,0 +1,46 @@ +.TH ENV 1 "24 September 2000" +.SH NAME +env, set \- Environment manipulation commands +.SH SYNOPSIS +env +.LP +set \fIenvironment-variable\fP=\fIvalue\fP +.SH DESCRIPTION +The kernel debugger contains an environment which contains a series +of name-value pairs. Some environment variables are known to the +various kernel debugger commands and have specific meaning to the +command; such are enumerated on the respective reference material. +.P +Arbitrary environment variables may be created and used with +many commands (those which require an \fIaddress-expression\fP). +.P +The +.B env +command is used to display the current environment. +.P +The +.B set +command is used to alter an existing environment variable or +establish a new environment variable. +.SH LIMITATIONS +There is a compile-time limit of 33 environment variables. +.P +There is a compile-time limit of 512 bytes (\fBKDB_ENVBUFSIZE\fP) +of heap space available for new environment variables and for +environment variables changed from their compile-time values. +.SH ENVIRONMENT +These commands explicitly manipulate the environment. +.SH SMP CONSIDERATIONS +None. +.SH USER SETTINGS +You can include "set" commands in kdb/kdb_cmds (see kdb.mm) to define +your environment variables at kernel startup. +.SH EXAMPLES +.TP 8 +env +Display current environment settings. + +.TP 8 +set IDCOUNT=100 +Set the number of lines to display for the \fBid\fP command +to the value \fI100\fP. diff -urN 2.4.0/Documentation/kdb/kdb_ll.man 2.4.0-ikd-1/Documentation/kdb/kdb_ll.man --- 2.4.0/Documentation/kdb/kdb_ll.man Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/Documentation/kdb/kdb_ll.man Mon Jan 15 18:40:04 2001 @@ -0,0 +1,134 @@ +.TH LL 1 "19 April 1999" +.SH NAME +ll \- Linked List examination +.SH SYNOPSIS +ll +.SH DESCRIPTION +The +.B ll +command is used to execute a single command repetitively for +each element of a linked list. +.P +The command specified by will be executed with a single +argument, the address of the current element. +.SH LIMITATIONS +Be careful if using this command recursively. +.SH ENVIRONMENT +None. +.SH SMP CONSIDERATIONS +None. +.SH EXAMPLES +.nf +.na +.ft CW +# cd modules +# insmod kdbm_vm.o +# Entering kdb on processor 0 due to PAUSE +kdb> ps +Task Addr Pid Parent cpu lcpu Tss Command +0xc03de000 0000000001 0000000000 0000 0000 0xc03de2d4 init +0xc0090000 0000000002 0000000001 0000 0000 0xc00902d4 kflushd +0xc000e000 0000000003 0000000001 0000 0000 0xc000e2d4 kpiod +0xc000c000 0000000004 0000000001 0000 0000 0xc000c2d4 kswapd +0xc7de2000 0000000056 0000000001 0000 0000 0xc7de22d4 kerneld +0xc7d3a000 0000000179 0000000001 0000 0000 0xc7d3a2d4 syslogd +0xc7a7e000 0000000188 0000000001 0000 0000 0xc7a7e2d4 klogd +0xc7a04000 0000000199 0000000001 0000 0000 0xc7a042d4 atd +0xc7b84000 0000000210 0000000001 0000 0000 0xc7b842d4 crond +0xc79d6000 0000000221 0000000001 0000 0000 0xc79d62d4 portmap +0xc798e000 0000000232 0000000001 0000 0000 0xc798e2d4 snmpd +0xc7904000 0000000244 0000000001 0000 0000 0xc79042d4 inetd +0xc78fc000 0000000255 0000000001 0000 0000 0xc78fc2d4 lpd +0xc77ec000 0000000270 0000000001 0000 0000 0xc77ec2d4 sendmail +0xc77b8000 0000000282 0000000001 0000 0000 0xc77b82d4 gpm +0xc7716000 0000000300 0000000001 0000 0000 0xc77162d4 smbd +0xc7ee2000 0000000322 0000000001 0000 0000 0xc7ee22d4 mingetty +0xc7d6e000 0000000323 0000000001 0000 0000 0xc7d6e2d4 login +0xc778c000 0000000324 0000000001 0000 0000 0xc778c2d4 mingetty +0xc78b6000 0000000325 0000000001 0000 0000 0xc78b62d4 mingetty +0xc77e8000 0000000326 0000000001 0000 0000 0xc77e82d4 mingetty +0xc7708000 0000000327 0000000001 0000 0000 0xc77082d4 mingetty +0xc770e000 0000000328 0000000001 0000 0000 0xc770e2d4 mingetty +0xc76b0000 0000000330 0000000001 0000 0000 0xc76b02d4 update +0xc7592000 0000000331 0000000323 0000 0000 0xc75922d4 ksh +0xc7546000 0000000338 0000000331 0000 0000 0xc75462d4 su +0xc74dc000 0000000339 0000000338 0000 0000 0xc74dc2d4 ksh +kdb> md 0xc74dc2d4 +c74dc2d4: 00000000 c74de000 00000018 00000000 .....`MG........ +c74dc2e4: 00000000 00000000 00000000 074de000 .............`M. +c74dc2f4: c01123ff 00000000 00000000 00000000 #.@............ +c74dc304: 00000000 00000000 c74dded0 00000000 ........P^MG.... +[omitted] +c74dc474: 00000000 00000000 00000000 00000000 ................ +c74dc484: 00000000 c7c15d00 c77b0900 c026fbe0 .....]AG..{G`{&@ +c74dc494: 00000000 c76c2000 00000000 00000000 ..... lG........ +c74dc4a4: 00000000 00000000 00000000 c74dc4ac ............,DMG +kdb> md 0xc026fbe0 +c026fbe0: c0262b60 00000000 c7594940 c74de000 @HYG....@IYG.`MG +[omitted] +kdb> md 0xc0262b60 +c0262b60: c0266660 08048000 0804c000 c7bec360 `f&@.....@..`C>G +kdb> ll c0262b60 12 md +c0262b60: c0266660 08048000 0804c000 c7bec360 `f&@.....@..`C>G +c7bec360: c0266660 0804c000 0804d000 c7becb20 `f&@.@...P.. K>G +c7becb20: c0266660 0804d000 08050000 c7bec3a0 `f&@.P...... C>G +c7bec3a0: c0266660 40000000 40009000 c7bec420 `f&@...@...@ D>G +c7bec420: c0266660 40009000 4000b000 c7bec4a0 `f&@...@.0.@ D>G +c7bec4a0: c0266660 4000b000 40010000 c7bec8e0 `f&@.0.@...@`H>G +c7bec8e0: c0266660 40010000 400a1000 c7becbe0 `f&@...@...@`K>G +c7becbe0: c0266660 400a1000 400a8000 c7becc60 `f&@...@...@`L>G +c7becc60: c0266660 400a8000 400b4000 c7952300 `f&@...@.@.@.#.G +c7952300: c0266660 400b5000 400bc000 c79521c0 `f&@.P.@.@.@@!.G +c79521c0: c0266660 400bc000 400bd000 c7bec6e0 `f&@.@.@.P.@`F>G +c7bec6e0: c0266660 bffff000 c0000000 00000000 `f&@.p?...@.... +kdb> +kdb> ll c0262b60 12 vm +struct vm_area_struct at 0xc0262b60 for 56 bytes +vm_start = 0x8048000 vm_end = 0x804c000 +page_prot = 0x25 avl_height = 2244 vm_offset = 0x0 +flags: READ EXEC MAYREAD MAYWRITE MAYEXEC DENYWRITE EXECUTABLE +struct vm_area_struct at 0xc7bec360 for 56 bytes +vm_start = 0x804c000 vm_end = 0x804d000 +page_prot = 0x25 avl_height = -31808 vm_offset = 0x3000 +flags: READ WRITE MAYREAD MAYWRITE MAYEXEC DENYWRITE EXECUTABLE +struct vm_area_struct at 0xc7becb20 for 56 bytes +vm_start = 0x804d000 vm_end = 0x8050000 +page_prot = 0x25 avl_height = -28664 vm_offset = 0x0 +flags: READ WRITE EXEC MAYREAD MAYWRITE MAYEXEC +struct vm_area_struct at 0xc7bec3a0 for 56 bytes +vm_start = 0x40000000 vm_end = 0x40009000 +page_prot = 0x25 avl_height = 30126 vm_offset = 0x0 +flags: READ EXEC MAYREAD MAYWRITE MAYEXEC DENYWRITE +struct vm_area_struct at 0xc7bec420 for 56 bytes +vm_start = 0x40009000 vm_end = 0x4000b000 +page_prot = 0x25 avl_height = 30126 vm_offset = 0x8000 +flags: READ WRITE MAYREAD MAYWRITE MAYEXEC DENYWRITE +struct vm_area_struct at 0xc7bec4a0 for 56 bytes +vm_start = 0x4000b000 vm_end = 0x40010000 +page_prot = 0x25 avl_height = 26853 vm_offset = 0x0 +flags: READ MAYREAD MAYWRITE MAYEXEC +struct vm_area_struct at 0xc7bec8e0 for 56 bytes +vm_start = 0x40010000 vm_end = 0x400a1000 +page_prot = 0x25 avl_height = 2244 vm_offset = 0x0 +flags: READ EXEC MAYREAD MAYWRITE MAYEXEC +struct vm_area_struct at 0xc7becbe0 for 56 bytes +vm_start = 0x400a1000 vm_end = 0x400a8000 +page_prot = 0x25 avl_height = 30126 vm_offset = 0x90000 +flags: READ WRITE MAYREAD MAYWRITE MAYEXEC +struct vm_area_struct at 0xc7becc60 for 56 bytes +vm_start = 0x400a8000 vm_end = 0x400b4000 +page_prot = 0x25 avl_height = 2244 vm_offset = 0x0 +flags: READ WRITE MAYREAD MAYWRITE MAYEXEC +struct vm_area_struct at 0xc7952300 for 56 bytes +vm_start = 0x400b5000 vm_end = 0x400bc000 +page_prot = 0x25 avl_height = 30126 vm_offset = 0x0 +flags: READ EXEC MAYREAD MAYWRITE MAYEXEC +struct vm_area_struct at 0xc79521c0 for 56 bytes +vm_start = 0x400bc000 vm_end = 0x400bd000 +page_prot = 0x25 avl_height = -16344 vm_offset = 0x6000 +flags: READ WRITE MAYREAD MAYWRITE MAYEXEC +struct vm_area_struct at 0xc7bec6e0 for 56 bytes +vm_start = 0xbffff000 vm_end = 0xc0000000 +page_prot = 0x25 avl_height = 2244 vm_offset = 0x0 +flags: READ WRITE EXEC MAYREAD MAYWRITE MAYEXEC GROWSDOWN +kdb> diff -urN 2.4.0/Documentation/kdb/kdb_md.man 2.4.0-ikd-1/Documentation/kdb/kdb_md.man --- 2.4.0/Documentation/kdb/kdb_md.man Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/Documentation/kdb/kdb_md.man Mon Jan 15 18:40:04 2001 @@ -0,0 +1,103 @@ +.TH MD 1 "09 March 1999" +.SH NAME +md, mdr, mds, mm\- Memory manipulation commands +.SH SYNOPSIS +md [ \fIaddress-expression\fP [ \fIline-count\fP [\fIoutput-radix\fP ] ] ] +.LP +mdr \fIaddress-expression\fP,\fIbytes\fP +.LP +mds [ \fIaddress-expression\fP [ \fIline-count\fP [\fIoutput-radix\fP ] ] ] +.LP +mm \fIaddress-expression\fP \fInew-contents\fP +.SH DESCRIPTION +The +.B md +command is used to display the contents of memory. +The \fIaddress-expression\fP may be a numeric value (decimal or +hexidecimal), a symbol name, a register name preceeded by one or more +percent symbols '%', an environment variable name preceeded by +a currency symbol '$', or a simple expression consisting of a +symbol name, an addition or subtraction character and a numeric +value (decimal or hexidecimal). +.P +If the \fIline-count\fP or \fIradix\fP arguments are omitted, +they default to the values of the \fBMDCOUNT\fP and \fBRADIX\fP +environment variables respectively. If the \fBMDCOUNT\fP or +\fBRADIX\fP environment variables are unset, the appropriate +defaults will be used [see \fBENVIRONMENT\fP below]. +.P +The +.B mdr +command displays the raw contents of memory, starting at the specified +address for the specified number of bytes. +The data is printed in one line without a leading address and no +trailing character conversion. +.B mdr +is intended for interfacing with external debuggers, it is of little +use to humans. +.P +The +.B mds +command displays the contents of memory one word per line and +attempts to correlate the contents of each word with a symbol +in the symbol table. If no symbol is found, the ascii representation +of the word is printed, otherwise the symbol name and offset from +symbol value are printed. +By default the section data is printed for kernel symbols. +.P +The +.B mm +command allows modification of memory. The word at the address +represented by \fIaddress-expression\fP is changed to +\fInew-contents\fP. \fInew-contents\fP is allowed to be an +\fIaddress-expression\fP. +.SH LIMITATIONS +None. +.SH ENVIRONMENT +.TP 8 +MDCOUNT +This environment variable (default=8) defines the number of lines +that will be displayed by each invocation of the \fBmd\fP command. + +.TP 8 +RADIX +This environment variable (default=16) defines the radix used to +print the memory contents. + +.TP 8 +BYTESPERWORD +This environment variable (default=4) selects the width of output +data when printing memory contents. Select the value two to get +16-bit word output, select the value one to get byte output. + +.TP 8 +LINES +This environment variable governs the number of lines of output +that will be presented before the kernel debugger built-in pager +pauses the output. This variable only affects the functioning +of the \fBmd\fP and \fBmds\fP if the \fBMDCOUNT\fP variable +is set to a value greater than the \fBLINES\fP variable. + +.TP 8 +If the \fBNOSECT\fP environment variable is non-zero then the +section information is suppressed. +.SH SMP CONSIDERATIONS +None. +.SH EXAMPLES +.TP 8 +md %edx +Display memory starting at the address contained in register \fB%edx\fP. + +.TP 8 +mds %esp +Display stack contents symbolically. This command is quite useful +in manual stack traceback. + +.TP 8 +mm 0xc0252110 0x25 +Change the memory location at 0xc0252110 to the value 0x25. + +.TP 8 +md chrdev_table 15 +Display 15 lines (at 16 bytes per line) starting at address +represented by the symbol \fIchrdev_table\fP. diff -urN 2.4.0/Documentation/kdb/kdb_rd.man 2.4.0-ikd-1/Documentation/kdb/kdb_rd.man --- 2.4.0/Documentation/kdb/kdb_rd.man Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/Documentation/kdb/kdb_rd.man Mon Jan 15 18:40:04 2001 @@ -0,0 +1,68 @@ +.TH RD 1 "09 March 1999" +.SH NAME +rd, rm\- Register manipulation commands +.SH SYNOPSIS +rd [c|d|u] +.LP +rm \fIregister-name\fP \fInew-contents\fP +.LP +ef
+.SH DESCRIPTION +The +.B rd +command is used to display the contents of processor registers. +Without any arguments, the rd command displays the contents of +the general register set at the point at which the kernel debugger +was entered. With the 'c' argument, the processor control registers +%cr0, %cr1, %cr2 and %cr4 are displayed, while with the 'd' argument +the processor debug registers are displayed. If the 'u' argument +is supplied, the registers for the current task as of the last +time the current task entered the kernel are displayed. +.P +The +.B rm +command allows modification of a register. The following +register names are valid: \fB%eax\fP, \fB%ebx\fP, \fB%ecx\fP, +\fB%edx\fP, \fB%esi\fP, \fB%edi\fP, \fB%esp\fP, \fB%eip\fP, +and \fB%ebp\fP. Note that if two '%' symbols are used +consecutively, the register set displayed by the 'u' argument +to the \fBrd\fP command is modified. +.P +The debug registers, \fBdr0\fP through \fBdr3\fP and both +\fBdr6\fP and \fBdr7\fP can also be modified with the \fBrm\fP +command. +.P +The +.B ef +command displays an exception frame at the specified address. +.SH LIMITATIONS +Currently the \fBrm\fP command will not allow modification of the +control registers. +.P +Currently neither the \fBrd\fP command nor the \fBrm\fP command will +display or modify the model specific registers on the Pentium +and Pentium Pro families. +.SH ENVIRONMENT +None. +.SH SMP CONSIDERATIONS +None. +.SH EXAMPLES +.TP 8 +rd +Display general register set. + +.TP 8 +rm %eax 0 +Set the contents of \fB%eax\fP to zero. This will be the +value of %eax when kdb returns from the condition which +invoked it. + +.TP 8 +rm %%eax 0 +Set the value of the \fB%eax\fP register to zero. This will +be the value the user-mode application will see upon returning +from the kernel. + +.TP 8 +rm dr0 0xc1287220 +Set the value of the \fBdr0\fB register to \f(CW0xc1287220\fP. diff -urN 2.4.0/Documentation/kdb/kdb_ss.man 2.4.0-ikd-1/Documentation/kdb/kdb_ss.man --- 2.4.0/Documentation/kdb/kdb_ss.man Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/Documentation/kdb/kdb_ss.man Mon Jan 15 18:40:04 2001 @@ -0,0 +1,101 @@ +.TH SS 1 "24 September 2000" +.SH NAME +ss, ssb \- Single Step +.SH SYNOPSIS +ss [] +.LP +ssb +.SH DESCRIPTION +The +.B ss +command is used to execute a single instruction and return +to the kernel debugger. +.P +Both the instruction that was single-stepped and the next +instruction to execute are printed. +.P +The \fBssb\fP command will execute instructions from the +current value of the instruction pointer. Each instruction +will be printed as it is executed; execution will stop at +any instruction which would cause the flow of control to +change (e.g. branch, call, interrupt instruction, return, etc.) +.SH LIMITATIONS +None. +.SH ENVIRONMENT +None. +.SH SMP CONSIDERATIONS +Other processors are held in the kernel debugger when the instruction +is traced. Single stepping though code that requires a lock which is +in use by another processor is an exercise in futility, it will never +succeed. +.SH INTERRUPT CONSIDERATIONS +When a kdb event occurs, one cpu (the initial cpu) enters kdb state. +It uses a cross system non maskable interrupt (NMI) to interrupt the +other cpus and bring them all into kdb state. All cpus run with +interrupts disabled while they are inside kdb, this prevents most +external events from disturbing the kernel while kdb is running. +.B Note: +Disabled interrupts means that any I/O that relies on interrupts cannot +proceed while kdb is in control, devices can time out. The clock tick +is also disabled, machines will lose track of time while they are +inside kdb. +.P +Even with interrupts disabled, some NMI events will still occur, these +can disturb the kernel while you are debugging it. The initial cpu +will still accept NMI events, assuming that kdb was not entered for an +NMI event. Any cpu where you use the SS or SSB commands will accept +NMI events, even after the instruction has finished and the cpu is back +in kdb. This is an unavoidable side effect of the fact that doing +SS[B] requires the cpu to drop all the way out of kdb, including +exiting from the NMI event that brought the cpu into kdb. Under normal +circumstances the only NMI event is for the NMI oopser and that is kdb +aware so it does not disturb the kernel while kdb is running. +.P +Sometimes doing SS or SSB on ix86 will allow one interrupt to proceed, +even though the cpu is disabled for interrupts. I have not been able +to track this one down but I suspect that the interrupt was pending +when kdb was entered and it runs when kdb exits through IRET even +though the popped flags are marked as cli(). If any ix86 hardware +expert can shed some light on this problem, please notify the kdb +maintainer. +.SH EXAMPLES +.nf +.na +.ft CW +kdb> bp gendisk_head datar 4 +Data Access Breakpoint #0 at 0xc024ddf4 (gendisk_head) in dr0 is enabled on cpu 0 +for 4 bytes +kdb> go +... +[root@host /root]# cat /proc/partitions +Entering kdb on processor 0 due to Debug Exception @ 0xc01845e3 +Read/Write breakpoint #0 at 0xc024ddf4 +[0]kdb> ssb +sd_finish+0x7b: movzbl 0xc02565d4,%edx +sd_finish+0x82: leal 0xf(%edx),%eax +sd_finish+0x85: sarl $0x4,%eax +sd_finish+0x88: movl 0xc0256654,%ecx +sd_finish+0x8e: leal (%eax,%eax,4),%edx +sd_finish+0x91: leal (%eax,%edx,2),%edx +sd_finish+0x94: movl 0xc0251108,%eax +sd_finish+0x99: movl %eax,0xffffffc(%ecx,%edx,4) +sd_finish+0x9d: movl %ecx,0xc0251108 +sd_finish+0xa3: xorl %ebx,%ebx +sd_finish+0xa5: cmpb $0x0,0xc02565d4 +[0]kdb> go +[root@host /root]# + +[0]kdb> ss +sys_read: pushl %ebp +SS trap at 0xc01274c1 +sys_read+0x1: movl %esp,%ebp +[0]kdb> ss +sys_read+0x1: movl %esp,%ebp +SS trap at 0xc01274c3 +sys_read+0x3: subl $0xc,%esp +[0]kdb> ss +sys_read+0x3: subl $0xc,%esp +SS trap at 0xc01274c6 +sys_read+0x6: pushl %edi +[0]kdb> + diff -urN 2.4.0/Documentation/ktrace.txt 2.4.0-ikd-1/Documentation/ktrace.txt --- 2.4.0/Documentation/ktrace.txt Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/Documentation/ktrace.txt Mon Jan 15 18:40:04 2001 @@ -0,0 +1,88 @@ +ktrace - Trace logic flow through the kernel with time stamps. + + +******* Please read debugging.txt first. ******* + + +LIMITATION: nanosecond accuracy timings on x86 CPUs works only if the + CPU has the rtdsc instruction. If you have another x86 + CPU, undef the HAVE_RTDSC define in include/asm/profiler.h. + See the 'tsc' flag in the /proc/cpuinfo flags field if + unsure. + + Alpha CPU support is not yet tested. + Intel SMP is tested + + +INSTALLATION + +If you are reading this, you have probably already applied the patch to +your kernel, now set the options and rebuild. Under Kernel Hacking, +say Y to Kernel debugging support then Y to Enable kernel tracing. +Make dep clean, recompile, install the new kernel and modules, reboot. + +Expect the new kernel to be somewhat slower than the unpatched kernel. +Check out /proc/trace, if it exists then you can go on to to the +user-space part: + +In /usr/src/linux, make debug. To get the current trace on a 166 MHz +CPU: + +scripts/ktrace --speed 166 --map /usr/src/linux/System.map > output.txt + +you should get something like this in output.txt: + +MHZ: 166. +read 4420 lines from System.map. +calibration done, estimated measurement latency: 0.34 microseconds. + +c01299ca put_unused_buffer_head + (0.90) +c011232b wake_up +<13/f0> (1.48) +c0129a26 get_more_buffer_heads + (0.61) +c012880f get_hash_table +<13/c0> (1.34) +c01296ca __brelse + (97.15) +c0129345 set_writetime + (0.11) +c0129398 refile_buffer +<10/334> (0.36) +[...] + +By default, all of the kernel except for init_task and the profiler +is traced. This can lead to a very busy trace file, full of +low level routines. To turn off tracing for a directory and all its +subdirectories, add the line + + override CFLAGS := $(CFLAGS:%-pg=%-g -c) + +to the relevant Makefile, before Rules.make. Delete the *.o files you +want to recompile and make zImage/modules. + +ktrace can get an exclusive lock on /proc/trace before reading it. +This allows ktrace to be suspended until an event occurs. For example, + +* User written program gets exclusive lock on /proc/trace, waits for + event to occur. + +* After starting above program, user runs ktrace with -l or --lock + options which suspends on the lock. + +* User written program detects the desired event, releases the lock. + +* ktrace runs, the resulting trace is as close to the event as + scheduling will allow. + +Sometimes you cannot read /proc/trace directly, typically because the +system is dead and ktrace cannot be run. If it is still responding to +the Magic-SysRQ key (you did select that option didn't you?) then +SysRQ-g dumps syslog and /proc/trace to all consoles, the latter is in +hex. Capture the output via a serial console on another machine +(another useful debugging option). + +After your dead machine has been restarted, take the captured hex dump +of /proc/trace and feed it to ktrace with the option "-d filename" or +"--dump filename". The lock option is ignored when reading a dumped +ktrace. + +Have fun, mail mingo@pc5829.hil.siemens.at if problems. + +Updated by: Mike Galbraith mikeg@weiden.de + +map option, dump option and kernel integration by Keith Owens . diff -urN 2.4.0/Documentation/nmi_watchdog.txt 2.4.0-ikd-1/Documentation/nmi_watchdog.txt --- 2.4.0/Documentation/nmi_watchdog.txt Mon Aug 21 17:57:35 2000 +++ 2.4.0-ikd-1/Documentation/nmi_watchdog.txt Mon Jan 15 18:40:04 2001 @@ -1,19 +1,30 @@ -Is your SMP system locking up unpredictably? No keyboard activity, just -a frustrating complete hard lockup? Do you want to help us debugging -such lockups? If all yes then this document is definitely for you. - -on Intel SMP hardware there is a feature that enables us to generate -'watchdog NMI interrupts'. (NMI: Non Maskable Interrupt - these get -executed even if the system is otherwise locked up hard) This can be -used to debug hard kernel lockups. By executing periodic NMI interrupts, -the kernel can monitor whether any CPU has locked up, and print out -debugging messages if so. You can enable/disable the NMI watchdog at boot -time with the 'nmi_watchdog=1' boot parameter. Eg. the relevant -lilo.conf entry: +Is your system locking up unpredictably? No keyboard activity, just a +frustrating complete hard lockup? Do you want to help us debugging +such lockups? If all yes then this document is definitely for you. + +On Intel SMP hardware there is a feature that enables us to generate +'watchdog NMI interrupts' (NMI: Non Maskable Interrupt - these get +executed even if the system is otherwise locked up hard). + +On Intel UP hardware which has a local APIC (Advanced Programmable +Interrupt Controller) with the ability to interrupt on performance +counter overflow (typically any P6 and above) can also generate +watchdog NMI events. + +This can be used to debug hard kernel lockups. By executing periodic +NMI interrupts, the kernel can monitor whether any CPU has locked up, +and print out debugging messages if so. You can enable/disable the NMI +watchdog at boot time with the 'nmi_watchdog=1' boot parameter. Eg. +the relevant lilo.conf entry: append="nmi_watchdog=1" +You can also activate or deactivate the NMI watchdog on the fly + + echo "0" > /proc/sys/kernel/nmi_watchdog - deactivate + echo "1" > /proc/sys/kernel/nmi_watchdog - activate + A 'lockup' is the following scenario: if any CPU in the system does not execute the period local timer interrupt for more than 5 seconds, then the NMI handler generates an oops and kills the process. This @@ -27,7 +38,36 @@ NOTE: currently the NMI-oopser is enabled unconditionally on x86 SMP boxes. +On uniprocessors, the NMI watchdog requires the dedicated use of MSR +performance counter 1 (PerfCtr1). If you are using PerfCtr1 for tuning +work, the UP NMI watchdog will conflict with your tuning. Therefore on +UP the watchdog is conditionally activated via CONFIG_UP_NMI_WATCHDOG. +Note that CONFIG_UP_NMI_WATCHDOG only controls the default setting for +UP, you can always override that setting by booting with +nmi_watchdog=0/1 or by + + echo "0/1" > /proc/sys/kernel/nmi_watchdog + +Activating or deactivating the NMI watchdog on UP will reset both +performance counters. PerfCtr0 will be turned off, PerfCtr1 will be +used to generate NMI events or will be turned off. After activating +the UP NMI watchdog, you can use PerfCtr0 but not PerfCtr1. After +deactivating, you can use both performance counters. Activating or +deactivating the NMI watchdog on SMP does not affect the performance +counters. + +The SMP and UP watchdogs run at different rates. On SMP each cpu gets +one NMI every timer tick. On UP, when the cpu gets an NMI depends on +how busy the cpu is. If the cpu is running at 100% busy then it gets +an NMI every timer tick, if it is running at 1% busy it gets NMI every +100 timer ticks, approximately. A hung machine is almost always in a +tight loop so the UP NMI watchdog will trigger after a short period. + +Changes for UP NMI watchdog and /proc/sys/kernel/nmi_watchdog by +Keith Owens September 26, 2000. Complain to + first for any problems. + +Original author: [ feel free to send bug reports, suggestions and patches to Ingo Molnar or the Linux SMP mailing list at ] - diff -urN 2.4.0/Documentation/sysrq.txt 2.4.0-ikd-1/Documentation/sysrq.txt --- 2.4.0/Documentation/sysrq.txt Fri Jul 28 21:50:52 2000 +++ 2.4.0-ikd-1/Documentation/sysrq.txt Mon Jan 15 18:40:04 2001 @@ -2,6 +2,7 @@ MAGIC SYSRQ KEY DOCUMENTATION v1.32 ------------------------------------ [Sat Apr 8 22:15:03 CEST 2000] + [Fri May 22 21:33:06 EST 1998 - add dumploGs, Oops] * What is the magic SysRQ key? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -42,7 +43,7 @@ 'b' - Will immediately reboot the system without syncing or unmounting your disks. -'o' - Will shut your system off (if configured and supported). +'f' - Will shut your system off (if configured and supported). 's' - Will attempt to sync all mounted filesystems. @@ -67,6 +68,10 @@ 'l' - Send a SIGKILL to all processes, INCLUDING init. (Your system will be non-functional after this.) +'g' - Dumps log files to all registered consoles. + +'o' - Force an Oops. + 'h' - Will display help ( actually any other key than those listed above will display help. but 'h' is easy to remember :-) @@ -107,6 +112,12 @@ t'E'rm and k'I'll are useful if you have some sort of runaway process you are unable to kill any other way, especially if it's spawning other processes. + +dumplo'G's is useful when the system is hung and you want to see the +log files. It is a good idea to have a serial console assigned to +capture the result. + +'O'ops forces an oops so you can get a kernel backtrace. * Sometimes SysRQ seems to get 'stuck' after using it, what can I do? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff -urN 2.4.0/Makefile 2.4.0-ikd-1/Makefile --- 2.4.0/Makefile Fri Jan 5 02:19:26 2001 +++ 2.4.0-ikd-1/Makefile Mon Jan 15 18:40:04 2001 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 0 -EXTRAVERSION = +EXTRAVERSION = -ikd KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -15,8 +15,13 @@ HPATH = $(TOPDIR)/include FINDHPATH = $(HPATH)/asm $(HPATH)/linux $(HPATH)/scsi $(HPATH)/net -HOSTCC = gcc -HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +ifndef KERNEL_CC + HOSTCC =gcc +else + HOSTCC = $(KERNEL_CC) +endif + +HOSTCFLAGS =-Wall -Wstrict-prototypes -O2 CROSS_COMPILE = @@ -26,7 +31,12 @@ AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld -CC = $(CROSS_COMPILE)gcc +ifndef KERNEL_CC + CC =$(CROSS_COMPILE)gcc +else + CC =$(CROSS_COMPILE)$(KERNEL_CC) +endif + CPP = $(CC) -E AR = $(CROSS_COMPILE)ar NM = $(CROSS_COMPILE)nm @@ -36,13 +46,16 @@ MAKEFILES = $(TOPDIR)/.config GENKSYMS = /sbin/genksyms DEPMOD = /sbin/depmod +KALLSYMS = /sbin/kallsyms MODFLAGS = -DMODULE CFLAGS_KERNEL = PERL = perl +AWK = awk +TMPPREFIX = export VERSION PATCHLEVEL SUBLEVEL EXTRAVERSION KERNELRELEASE ARCH \ CONFIG_SHELL TOPDIR HPATH HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \ - CPP AR NM STRIP OBJCOPY OBJDUMP MAKE MAKEFILES GENKSYMS MODFLAGS PERL + CPP AR NM STRIP OBJCOPY OBJDUMP MAKE MAKEFILES GENKSYMS MODFLAGS PERL AWK all: do-it-all @@ -87,7 +100,16 @@ CPPFLAGS := -D__KERNEL__ -I$(HPATH) -CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing +CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fno-strict-aliasing $(KERNEL_CFLAGS) +ifeq ($(CONFIG_KERNEL_DEBUGGING),y) + # Profiling is a big overhead so only turn it on if the user really wants it. + ifeq ($(CONFIG_DEBUG_MCOUNT),y) + CFLAGS += -pg + endif +else + CFLAGS += -fomit-frame-pointer +endif + AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS) # @@ -127,6 +149,15 @@ LIBS =$(TOPDIR)/lib/lib.a SUBDIRS =kernel drivers mm fs net ipc lib +ifeq ($(CONFIG_KERNEL_DEBUGGING),y) + SUBDIRS += kernel/debug + CORE_FILES +=kernel/debug/debug.o + ifeq ($(CONFIG_KDB),y) + CORE_FILES += kdb/kdb.o + SUBDIRS += kdb + endif +endif + DRIVERS-n := DRIVERS-y := DRIVERS-m := @@ -185,7 +216,7 @@ CLEAN_FILES = \ kernel/ksyms.lst include/linux/compile.h \ vmlinux System.map \ - .tmp* \ + $(TMPPREFIX).tmp* \ drivers/char/consolemap_deftbl.c drivers/video/promcon_tbl.c \ drivers/char/conmakehash \ drivers/char/drm/*-mod.c \ @@ -218,6 +249,7 @@ scripts/lxdialog/*.o scripts/lxdialog/lxdialog \ .menuconfig.log \ include/asm \ + kdb/gen-kdb_cmds.c \ .hdepend scripts/mkdep scripts/split-include scripts/docproc \ $(TOPDIR)/include/linux/modversions.h # directories removed with 'make mrproper' @@ -228,14 +260,14 @@ include arch/$(ARCH)/Makefile -export CPPFLAGS CFLAGS AFLAGS +export CPPFLAGS CFLAGS CFLAGS_KERNEL AFLAGS AFLAGS_KERNEL export NETWORKS DRIVERS LIBS HEAD LDFLAGS LINKFLAGS MAKEBOOT ASFLAGS .S.s: - $(CPP) $(AFLAGS) -traditional -o $*.s $< + $(CPP) $(AFLAGS) $(AFLAGS_KERNEL) -traditional -o $*.s $< .S.o: - $(CC) $(AFLAGS) -traditional -c -o $*.o $< + $(CC) $(AFLAGS) $(AFLAGS_KERNEL) -traditional -c -o $*.o $< Version: dummy @rm -f include/linux/compile.h @@ -243,16 +275,39 @@ boot: vmlinux @$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C arch/$(ARCH)/boot +LD_VMLINUX := $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \ + --start-group \ + $(CORE_FILES) \ + $(DRIVERS) \ + $(NETWORKS) \ + $(LIBS) \ + --end-group +ifeq ($(CONFIG_KALLSYMS),y) +LD_VMLINUX_KALLSYMS := $(TMPPREFIX).tmp_kallsyms3.o +else +LD_VMLINUX_KALLSYMS := +endif + vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs - $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \ - --start-group \ - $(CORE_FILES) \ - $(DRIVERS) \ - $(NETWORKS) \ - $(LIBS) \ - --end-group \ - -o vmlinux + @$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" kallsyms + +.PHONY: kallsyms + +kallsyms: +ifeq ($(CONFIG_KALLSYMS),y) + @echo kallsyms pass 1 + $(LD_VMLINUX) -o $(TMPPREFIX).tmp_vmlinux1 + @$(KALLSYMS) $(TMPPREFIX).tmp_vmlinux1 > $(TMPPREFIX).tmp_kallsyms1.o + @echo kallsyms pass 2 + @$(LD_VMLINUX) $(TMPPREFIX).tmp_kallsyms1.o -o $(TMPPREFIX).tmp_vmlinux2 + @$(KALLSYMS) $(TMPPREFIX).tmp_vmlinux2 > $(TMPPREFIX).tmp_kallsyms2.o + @echo kallsyms pass 3 + @$(LD_VMLINUX) $(TMPPREFIX).tmp_kallsyms2.o -o $(TMPPREFIX).tmp_vmlinux3 + @$(KALLSYMS) $(TMPPREFIX).tmp_vmlinux3 > $(TMPPREFIX).tmp_kallsyms3.o +endif + $(LD_VMLINUX) $(LD_VMLINUX_KALLSYMS) -o vmlinux $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map + @rm -f $(TMPPREFIX).tmp_vmlinux* $(TMPPREFIX).tmp_kallsyms* symlinks: rm -f include/asm @@ -279,6 +334,11 @@ scripts/split-include include/linux/autoconf.h include/config @ touch include/config/MARKER +debug: include/linux/version.h + $(MAKE) -C scripts ktrace + $(MAKE) -C scripts/memleak all + $(MAKE) -C scripts/lockstat all + linuxsubdirs: $(patsubst %, _dir_%, $(SUBDIRS)) $(patsubst %, _dir_%, $(SUBDIRS)) : dummy include/linux/version.h include/config/MARKER @@ -406,6 +466,9 @@ rm -f $(CLEAN_FILES) rm -rf $(CLEAN_DIRS) $(MAKE) -C Documentation/DocBook clean + $(MAKE) -C scripts clean + $(MAKE) -C scripts/memleak clean + $(MAKE) -C scripts/lockstat clean mrproper: clean archmrproper find . \( -size 0 -o -name .depend \) -type f -print | xargs rm -f @@ -444,6 +507,8 @@ scripts/mkdep init/*.c > .depend scripts/mkdep `find $(FINDHPATH) -name SCCS -prune -o -follow -name \*.h ! -name modversions.h -print` > .hdepend $(MAKE) $(patsubst %,_sfdep_%,$(SUBDIRS)) _FASTDEP_ALL_SUB_DIRS="$(SUBDIRS)" + $(MAKE) $(patsubst %,_sfdep_%,$(SUBDIRS) scripts) _FASTDEP_ALL_SUB_DIRS="$(SUBDIRS) scripts" + ifdef CONFIG_MODVERSIONS $(MAKE) update-modverfile endif diff -urN 2.4.0/arch/alpha/config.in 2.4.0-ikd-1/arch/alpha/config.in --- 2.4.0/arch/alpha/config.in Tue Jan 2 17:41:12 2001 +++ 2.4.0-ikd-1/arch/alpha/config.in Mon Jan 15 18:40:04 2001 @@ -359,7 +359,16 @@ fi bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ + + bool 'Legacy kernel start address' CONFIG_ALPHA_LEGACY_START_ADDRESS -bool 'Legacy kernel start address' CONFIG_ALPHA_LEGACY_START_ADDRESS +source kernel/debug/Config.in + +# arch specific debugging options +if [ "$CONFIG_KERNEL_DEBUGGING" = "y" ]; then + if [ "$CONFIG_SMP" = "y" ]; then + bool ' Kernel Lock Metering' CONFIG_LOCKMETER + fi +fi endmenu diff -urN 2.4.0/arch/alpha/kernel/entry.S 2.4.0-ikd-1/arch/alpha/kernel/entry.S --- 2.4.0/arch/alpha/kernel/entry.S Sun Sep 3 20:36:45 2000 +++ 2.4.0-ikd-1/arch/alpha/kernel/entry.S Mon Jan 15 18:40:04 2001 @@ -119,6 +119,17 @@ ldq $28,144($30); \ addq $30,184,$30 +/* + * Conditionally do profiling + */ +#ifdef CONFIG_TRACE +#define CALL_MCOUNT \ + lda $28,_mcount; \ + jsr $28,($28),_mcount +#else +#define CALL_MCOUNT +#endif + .text .set noat #if defined(__linux__) && !defined(__ELF__) @@ -141,6 +152,8 @@ .ent entMM entMM: SAVE_ALL + ldq $8,current_set + CALL_MCOUNT /* save $9 - $15 so the inline exception code can manipulate them. */ subq $30,56,$30 stq $9,0($30) @@ -391,6 +404,11 @@ .ent entUna entUna: lda $30,-256($30) +#ifdef CONFIG_TRACE + stq $8,64($30) + ldq $8,current_set +#endif + CALL_MCOUNT stq $0,0($30) ldq $0,256($30) /* get PS */ stq $1,8($30) @@ -402,6 +420,10 @@ stq $5,40($30) stq $6,48($30) stq $7,56($30) +#ifndef CONFIG_TRACE + stq $8,64($30) + ldq $8,current_set +#endif stq $8,64($30) stq $9,72($30) stq $10,80($30) @@ -462,6 +484,9 @@ .ent entUnaUser entUnaUser: ldq $0,0($30) /* restore original $0 */ +#ifdef CONFIG_TRACE + ldq $8,64($30) +#endif lda $30,256($30) /* pop entUna's stack frame */ SAVE_ALL /* setup normal kernel stack */ lda $30,-56($30) @@ -597,6 +622,7 @@ beq $4,restore_all bne $5,signal_return restore_all: + CALL_MCOUNT RESTORE_ALL call_pal PAL_rti diff -urN 2.4.0/arch/alpha/lib/Makefile 2.4.0-ikd-1/arch/alpha/lib/Makefile --- 2.4.0/arch/alpha/lib/Makefile Tue Jan 2 17:41:12 2001 +++ 2.4.0-ikd-1/arch/alpha/lib/Makefile Mon Jan 15 18:40:04 2001 @@ -46,6 +46,10 @@ fpreg.o \ callback_srm.o srm_puts.o srm_printk.o +ifeq ($(CONFIG_KERNEL_DEBUGGING),y) + OBJS += _mcount.o +endif + lib.a: $(OBJS) $(AR) rcs lib.a $(OBJS) diff -urN 2.4.0/arch/alpha/mm/fault.c 2.4.0-ikd-1/arch/alpha/mm/fault.c --- 2.4.0/arch/alpha/mm/fault.c Tue Jan 2 17:41:12 2001 +++ 2.4.0-ikd-1/arch/alpha/mm/fault.c Mon Jan 15 18:40:04 2001 @@ -180,6 +180,8 @@ return; } + /* recursion is the curse of the programming classes */ + SUSPEND_MCOUNT_PROC(current); /* * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. diff -urN 2.4.0/arch/alpha/vmlinux.lds.in 2.4.0-ikd-1/arch/alpha/vmlinux.lds.in --- 2.4.0/arch/alpha/vmlinux.lds.in Mon Jun 26 20:26:56 2000 +++ 2.4.0-ikd-1/arch/alpha/vmlinux.lds.in Mon Jan 15 18:40:04 2001 @@ -28,6 +28,10 @@ __stop___ksymtab = .; .kstrtab : { *(.kstrtab) } + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + /* Startup code */ . = ALIGN(8192); __init_begin = .; diff -urN 2.4.0/arch/arm/vmlinux-armo.lds.in 2.4.0-ikd-1/arch/arm/vmlinux-armo.lds.in --- 2.4.0/arch/arm/vmlinux-armo.lds.in Thu Dec 14 22:33:59 2000 +++ 2.4.0-ikd-1/arch/arm/vmlinux-armo.lds.in Mon Jan 15 18:40:04 2001 @@ -57,6 +57,10 @@ *(__ksymtab) __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + *(__kallsyms) + __stop___kallsyms = .; + *(.got) /* Global offset table */ _etext = .; /* End of text section */ diff -urN 2.4.0/arch/arm/vmlinux-armv.lds.in 2.4.0-ikd-1/arch/arm/vmlinux-armv.lds.in --- 2.4.0/arch/arm/vmlinux-armv.lds.in Thu Dec 14 22:33:59 2000 +++ 2.4.0-ikd-1/arch/arm/vmlinux-armv.lds.in Mon Jan 15 18:40:04 2001 @@ -52,6 +52,10 @@ *(__ksymtab) __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + *(__kallsyms) + __stop___kallsyms = .; + *(.got) /* Global offset table */ _etext = .; /* End of text section */ diff -urN 2.4.0/arch/i386/Makefile 2.4.0-ikd-1/arch/i386/Makefile --- 2.4.0/arch/i386/Makefile Tue Jan 2 17:41:12 2001 +++ 2.4.0-ikd-1/arch/i386/Makefile Mon Jan 15 18:40:04 2001 @@ -89,6 +89,11 @@ CORE_FILES := arch/i386/kernel/kernel.o arch/i386/mm/mm.o $(CORE_FILES) LIBS := $(TOPDIR)/arch/i386/lib/lib.a $(LIBS) $(TOPDIR)/arch/i386/lib/lib.a +ifdef CONFIG_KDB +LIBS := $(LIBS) $(TOPDIR)/arch/i386/kdb/kdba.o +SUBDIRS := $(SUBDIRS) arch/i386/kdb +endif + ifdef CONFIG_MATH_EMULATION SUBDIRS += arch/i386/math-emu DRIVERS += arch/i386/math-emu/math.o @@ -99,6 +104,11 @@ arch/i386/mm: dummy $(MAKE) linuxsubdirs SUBDIRS=arch/i386/mm + +ifdef CONFIG_KDB +arch/i386/kdb: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/i386/kdb +endif MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot diff -urN 2.4.0/arch/i386/config.in 2.4.0-ikd-1/arch/i386/config.in --- 2.4.0/arch/i386/config.in Tue Jan 2 17:41:12 2001 +++ 2.4.0-ikd-1/arch/i386/config.in Mon Jan 15 18:40:04 2001 @@ -168,11 +168,12 @@ if [ "$CONFIG_X86_UP_IOAPIC" = "y" ]; then define_bool CONFIG_X86_IO_APIC y define_bool CONFIG_X86_LOCAL_APIC y + bool ' NMI watchdog active for uniprocessors' CONFIG_UP_NMI_WATCHDOG fi fi if [ "$CONFIG_SMP" = "y" -a "$CONFIG_X86_CMPXCHG" = "y" ]; then - define_bool CONFIG_HAVE_DEC_LOCK y + define_bool CONFIG_HAVE_DEC_LOCK y fi endmenu @@ -374,4 +375,25 @@ #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ + +source kernel/debug/Config.in + +# arch specific debugging options +if [ "$CONFIG_KERNEL_DEBUGGING" = "y" ]; then + bool ' Print %eip to resolve symbols from locks' CONFIG_PRINT_EIP n + if [ "$CONFIG_NOHIGHMEM" = "y" ]; then + bool ' Kernel memory leak detection support' CONFIG_MEMLEAK n + fi + bool ' Built-in Kernel Debugger support' CONFIG_KDB n + if [ "$CONFIG_KDB" = "y" ]; then + bool ' KDB off by default' CONFIG_KDB_OFF + define_bool CONFIG_KALLSYMS y + define_bool CONFIG_FRAME_POINTER y + else + bool ' Load all symbols for debugging' CONFIG_KALLSYMS + fi + if [ "$CONFIG_SMP" = "y" ]; then + bool ' Kernel lock metering' CONFIG_LOCKMETER + fi +fi endmenu diff -urN 2.4.0/arch/i386/kdb/Makefile 2.4.0-ikd-1/arch/i386/kdb/Makefile --- 2.4.0/arch/i386/kdb/Makefile Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/arch/i386/kdb/Makefile Mon Jan 15 18:40:04 2001 @@ -0,0 +1,6 @@ +O_TARGET := kdba.o +obj-y := kdba_bt.o kdba_bp.o kdba_id.o kdba_io.o kdbasupport.o i386-dis.o + +override CFLAGS := $(CFLAGS:%-pg=% ) + +include $(TOPDIR)/Rules.make diff -urN 2.4.0/arch/i386/kdb/i386-dis.c 2.4.0-ikd-1/arch/i386/kdb/i386-dis.c --- 2.4.0/arch/i386/kdb/i386-dis.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/arch/i386/kdb/i386-dis.c Mon Jan 15 18:40:04 2001 @@ -0,0 +1,3781 @@ +/* Print i386 instructions for GDB, the GNU debugger. + Copyright (C) 1988, 89, 91, 93, 94, 95, 96, 97, 98, 1999 + Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +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. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* + * 80386 instruction printer by Pace Willisson (pace@prep.ai.mit.edu) + * July 1988 + * modified by John Hassey (hassey@dg-rtp.dg.com) + */ + +/* Extracted from cygnus CVS and modified for kdb use. + * Keith Owens 30 Oct 2000 + */ + +/* + * The main tables describing the instructions is essentially a copy + * of the "Opcode Map" chapter (Appendix A) of the Intel 80386 + * Programmers Manual. Usually, there is a capital letter, followed + * by a small letter. The capital letter tell the addressing mode, + * and the small letter tells about the operand size. Refer to + * the Intel manual for details. + */ + +#ifdef __KERNEL__ +#include +#include +#include +#include +#else +#include "dis-asm.h" +#include "sysdep.h" +#include "opintl.h" +#endif + +#define MAXLEN 20 + +#ifndef __KERNEL__ +#include +#endif + +#ifndef UNIXWARE_COMPAT +/* Set non-zero for broken, compatible instructions. Set to zero for + non-broken opcodes. */ +#define UNIXWARE_COMPAT 1 +#endif + +static int fetch_data PARAMS ((struct disassemble_info *, bfd_byte *)); + +struct dis_private +{ + /* Points to first byte not fetched. */ + bfd_byte *max_fetched; + bfd_byte the_buffer[MAXLEN]; + bfd_vma insn_start; +#ifndef __KERNEL__ + jmp_buf bailout; +#endif +}; + +/* The opcode for the fwait instruction, which we treat as a prefix + when we can. */ +#define FWAIT_OPCODE (0x9b) + +/* Flags for the prefixes for the current instruction. See below. */ +static int prefixes; + +/* Flags for prefixes which we somehow handled when printing the + current instruction. */ +static int used_prefixes; + +/* Flags stored in PREFIXES. */ +#define PREFIX_REPZ 1 +#define PREFIX_REPNZ 2 +#define PREFIX_LOCK 4 +#define PREFIX_CS 8 +#define PREFIX_SS 0x10 +#define PREFIX_DS 0x20 +#define PREFIX_ES 0x40 +#define PREFIX_FS 0x80 +#define PREFIX_GS 0x100 +#define PREFIX_DATA 0x200 +#define PREFIX_ADDR 0x400 +#define PREFIX_FWAIT 0x800 + +/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive) + to ADDR (exclusive) are valid. Returns 1 for success, longjmps + on error. */ +#define FETCH_DATA(info, addr) \ + ((addr) <= ((struct dis_private *)(info->private_data))->max_fetched \ + ? 1 : fetch_data ((info), (addr))) + +static int +fetch_data (info, addr) + struct disassemble_info *info; + bfd_byte *addr; +{ + int status; + struct dis_private *priv = (struct dis_private *)info->private_data; + bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer); + + status = (*info->read_memory_func) (start, + priv->max_fetched, + addr - priv->max_fetched, + info); + if (status != 0) + { + /* If we did manage to read at least one byte, then + print_insn_i386 will do something sensible. Otherwise, print + an error. We do that here because this is where we know + STATUS. */ + if (priv->max_fetched == priv->the_buffer) + (*info->memory_error_func) (status, start, info); +#ifndef __KERNEL__ + longjmp (priv->bailout, 1); +#else + /* XXX - what to do? */ + kdb_printf("Hmm. longjmp.\n"); +#endif + } + else + priv->max_fetched = addr; + return 1; +} + +#define XX NULL, 0 + +#define Eb OP_E, b_mode +#define indirEb OP_indirE, b_mode +#define Gb OP_G, b_mode +#define Ev OP_E, v_mode +#define Ed OP_E, d_mode +#define indirEv OP_indirE, v_mode +#define Ew OP_E, w_mode +#define Ma OP_E, v_mode +#define M OP_E, 0 /* lea */ +#define Mp OP_E, 0 /* 32 or 48 bit memory operand for LDS, LES etc */ +#define Gv OP_G, v_mode +#define Gw OP_G, w_mode +#define Rd OP_Rd, d_mode +#define Ib OP_I, b_mode +#define sIb OP_sI, b_mode /* sign extened byte */ +#define Iv OP_I, v_mode +#define Iw OP_I, w_mode +#define Jb OP_J, b_mode +#define Jv OP_J, v_mode +#define Cd OP_C, d_mode +#define Dd OP_D, d_mode +#define Td OP_T, d_mode + +#define eAX OP_REG, eAX_reg +#define eBX OP_REG, eBX_reg +#define eCX OP_REG, eCX_reg +#define eDX OP_REG, eDX_reg +#define eSP OP_REG, eSP_reg +#define eBP OP_REG, eBP_reg +#define eSI OP_REG, eSI_reg +#define eDI OP_REG, eDI_reg +#define AL OP_REG, al_reg +#define CL OP_REG, cl_reg +#define DL OP_REG, dl_reg +#define BL OP_REG, bl_reg +#define AH OP_REG, ah_reg +#define CH OP_REG, ch_reg +#define DH OP_REG, dh_reg +#define BH OP_REG, bh_reg +#define AX OP_REG, ax_reg +#define DX OP_REG, dx_reg +#define indirDX OP_REG, indir_dx_reg + +#define Sw OP_SEG, w_mode +#define Ap OP_DIR, 0 +#define Ob OP_OFF, b_mode +#define Ov OP_OFF, v_mode +#define Xb OP_DSreg, eSI_reg +#define Xv OP_DSreg, eSI_reg +#define Yb OP_ESreg, eDI_reg +#define Yv OP_ESreg, eDI_reg +#define DSBX OP_DSreg, eBX_reg + +#define es OP_REG, es_reg +#define ss OP_REG, ss_reg +#define cs OP_REG, cs_reg +#define ds OP_REG, ds_reg +#define fs OP_REG, fs_reg +#define gs OP_REG, gs_reg + +#define MX OP_MMX, 0 +#define XM OP_XMM, 0 +#define EM OP_EM, v_mode +#define EX OP_EX, v_mode +#define MS OP_MS, v_mode +#define None OP_E, 0 +#define OPSUF OP_3DNowSuffix, 0 +#define OPSIMD OP_SIMD_Suffix, 0 + +/* bits in sizeflag */ +#if 0 /* leave undefined until someone adds the extra flag to objdump */ +#define SUFFIX_ALWAYS 4 +#endif +#define AFLAG 2 +#define DFLAG 1 + +typedef void (*op_rtn) PARAMS ((int bytemode, int sizeflag)); + +static void OP_E PARAMS ((int, int)); +static void OP_G PARAMS ((int, int)); +static void OP_I PARAMS ((int, int)); +static void OP_indirE PARAMS ((int, int)); +static void OP_sI PARAMS ((int, int)); +static void OP_REG PARAMS ((int, int)); +static void OP_J PARAMS ((int, int)); +static void OP_DIR PARAMS ((int, int)); +static void OP_OFF PARAMS ((int, int)); +static void OP_ESreg PARAMS ((int, int)); +static void OP_DSreg PARAMS ((int, int)); +static void OP_SEG PARAMS ((int, int)); +static void OP_C PARAMS ((int, int)); +static void OP_D PARAMS ((int, int)); +static void OP_T PARAMS ((int, int)); +static void OP_Rd PARAMS ((int, int)); +static void OP_ST PARAMS ((int, int)); +static void OP_STi PARAMS ((int, int)); +static void OP_MMX PARAMS ((int, int)); +static void OP_XMM PARAMS ((int, int)); +static void OP_EM PARAMS ((int, int)); +static void OP_EX PARAMS ((int, int)); +static void OP_MS PARAMS ((int, int)); +static void OP_3DNowSuffix PARAMS ((int, int)); +static void OP_SIMD_Suffix PARAMS ((int, int)); +static void SIMD_Fixup PARAMS ((int, int)); + +static void append_seg PARAMS ((void)); +static void set_op PARAMS ((unsigned int op)); +static void putop PARAMS ((const char *template, int sizeflag)); +static void dofloat PARAMS ((int sizeflag)); +static int get16 PARAMS ((void)); +static int get32 PARAMS ((void)); +static void ckprefix PARAMS ((void)); +static const char *prefix_name PARAMS ((int, int)); +static void ptr_reg PARAMS ((int, int)); +static void BadOp PARAMS ((void)); + +#define b_mode 1 +#define v_mode 2 +#define w_mode 3 +#define d_mode 4 +#define x_mode 5 + +#define es_reg 100 +#define cs_reg 101 +#define ss_reg 102 +#define ds_reg 103 +#define fs_reg 104 +#define gs_reg 105 + +#define eAX_reg 108 +#define eCX_reg 109 +#define eDX_reg 110 +#define eBX_reg 111 +#define eSP_reg 112 +#define eBP_reg 113 +#define eSI_reg 114 +#define eDI_reg 115 + +#define al_reg 116 +#define cl_reg 117 +#define dl_reg 118 +#define bl_reg 119 +#define ah_reg 120 +#define ch_reg 121 +#define dh_reg 122 +#define bh_reg 123 + +#define ax_reg 124 +#define cx_reg 125 +#define dx_reg 126 +#define bx_reg 127 +#define sp_reg 128 +#define bp_reg 129 +#define si_reg 130 +#define di_reg 131 + +#define indir_dx_reg 150 + +#define USE_GROUPS 1 +#define USE_PREFIX_USER_TABLE 2 + +#define GRP1b NULL, NULL, 0, NULL, USE_GROUPS, NULL, 0 +#define GRP1S NULL, NULL, 1, NULL, USE_GROUPS, NULL, 0 +#define GRP1Ss NULL, NULL, 2, NULL, USE_GROUPS, NULL, 0 +#define GRP2b NULL, NULL, 3, NULL, USE_GROUPS, NULL, 0 +#define GRP2S NULL, NULL, 4, NULL, USE_GROUPS, NULL, 0 +#define GRP2b_one NULL, NULL, 5, NULL, USE_GROUPS, NULL, 0 +#define GRP2S_one NULL, NULL, 6, NULL, USE_GROUPS, NULL, 0 +#define GRP2b_cl NULL, NULL, 7, NULL, USE_GROUPS, NULL, 0 +#define GRP2S_cl NULL, NULL, 8, NULL, USE_GROUPS, NULL, 0 +#define GRP3b NULL, NULL, 9, NULL, USE_GROUPS, NULL, 0 +#define GRP3S NULL, NULL, 10, NULL, USE_GROUPS, NULL, 0 +#define GRP4 NULL, NULL, 11, NULL, USE_GROUPS, NULL, 0 +#define GRP5 NULL, NULL, 12, NULL, USE_GROUPS, NULL, 0 +#define GRP6 NULL, NULL, 13, NULL, USE_GROUPS, NULL, 0 +#define GRP7 NULL, NULL, 14, NULL, USE_GROUPS, NULL, 0 +#define GRP8 NULL, NULL, 15, NULL, USE_GROUPS, NULL, 0 +#define GRP9 NULL, NULL, 16, NULL, USE_GROUPS, NULL, 0 +#define GRP10 NULL, NULL, 17, NULL, USE_GROUPS, NULL, 0 +#define GRP11 NULL, NULL, 18, NULL, USE_GROUPS, NULL, 0 +#define GRP12 NULL, NULL, 19, NULL, USE_GROUPS, NULL, 0 +#define GRP13 NULL, NULL, 20, NULL, USE_GROUPS, NULL, 0 +#define GRP14 NULL, NULL, 21, NULL, USE_GROUPS, NULL, 0 +#define GRPAMD NULL, NULL, 22, NULL, USE_GROUPS, NULL, 0 + +#define PREGRP0 NULL, NULL, 0, NULL, USE_PREFIX_USER_TABLE, NULL, 0 +#define PREGRP1 NULL, NULL, 1, NULL, USE_PREFIX_USER_TABLE, NULL, 0 +#define PREGRP2 NULL, NULL, 2, NULL, USE_PREFIX_USER_TABLE, NULL, 0 +#define PREGRP3 NULL, NULL, 3, NULL, USE_PREFIX_USER_TABLE, NULL, 0 +#define PREGRP4 NULL, NULL, 4, NULL, USE_PREFIX_USER_TABLE, NULL, 0 +#define PREGRP5 NULL, NULL, 5, NULL, USE_PREFIX_USER_TABLE, NULL, 0 +#define PREGRP6 NULL, NULL, 6, NULL, USE_PREFIX_USER_TABLE, NULL, 0 +#define PREGRP7 NULL, NULL, 7, NULL, USE_PREFIX_USER_TABLE, NULL, 0 +#define PREGRP8 NULL, NULL, 8, NULL, USE_PREFIX_USER_TABLE, NULL, 0 +#define PREGRP9 NULL, NULL, 9, NULL, USE_PREFIX_USER_TABLE, NULL, 0 +#define PREGRP10 NULL, NULL, 10, NULL, USE_PREFIX_USER_TABLE, NULL, 0 +#define PREGRP11 NULL, NULL, 11, NULL, USE_PREFIX_USER_TABLE, NULL, 0 +#define PREGRP12 NULL, NULL, 12, NULL, USE_PREFIX_USER_TABLE, NULL, 0 +#define PREGRP13 NULL, NULL, 13, NULL, USE_PREFIX_USER_TABLE, NULL, 0 +#define PREGRP14 NULL, NULL, 14, NULL, USE_PREFIX_USER_TABLE, NULL, 0 + +#define FLOATCODE 50 +#define FLOAT NULL, NULL, FLOATCODE, NULL, 0, NULL, 0 + +struct dis386 { + const char *name; + op_rtn op1; + int bytemode1; + op_rtn op2; + int bytemode2; + op_rtn op3; + int bytemode3; +}; + +/* Upper case letters in the instruction names here are macros. + 'A' => print 'b' if no register operands or suffix_always is true + 'B' => print 'b' if suffix_always is true + 'E' => print 'e' if 32-bit form of jcxz + 'L' => print 'l' if suffix_always is true + 'N' => print 'n' if instruction has no wait "prefix" + 'P' => print 'w' or 'l' if instruction has an operand size prefix, + or suffix_always is true + 'Q' => print 'w' or 'l' if no register operands or suffix_always is true + 'R' => print 'w' or 'l' ("wd" or "dq" in intel mode) + 'S' => print 'w' or 'l' if suffix_always is true + 'W' => print 'b' or 'w' ("w" or "de" in intel mode) +*/ + +static const struct dis386 dis386_att[] = { + /* 00 */ + { "addB", Eb, Gb, XX }, + { "addS", Ev, Gv, XX }, + { "addB", Gb, Eb, XX }, + { "addS", Gv, Ev, XX }, + { "addB", AL, Ib, XX }, + { "addS", eAX, Iv, XX }, + { "pushP", es, XX, XX }, + { "popP", es, XX, XX }, + /* 08 */ + { "orB", Eb, Gb, XX }, + { "orS", Ev, Gv, XX }, + { "orB", Gb, Eb, XX }, + { "orS", Gv, Ev, XX }, + { "orB", AL, Ib, XX }, + { "orS", eAX, Iv, XX }, + { "pushP", cs, XX, XX }, + { "(bad)", XX, XX, XX }, /* 0x0f extended opcode escape */ + /* 10 */ + { "adcB", Eb, Gb, XX }, + { "adcS", Ev, Gv, XX }, + { "adcB", Gb, Eb, XX }, + { "adcS", Gv, Ev, XX }, + { "adcB", AL, Ib, XX }, + { "adcS", eAX, Iv, XX }, + { "pushP", ss, XX, XX }, + { "popP", ss, XX, XX }, + /* 18 */ + { "sbbB", Eb, Gb, XX }, + { "sbbS", Ev, Gv, XX }, + { "sbbB", Gb, Eb, XX }, + { "sbbS", Gv, Ev, XX }, + { "sbbB", AL, Ib, XX }, + { "sbbS", eAX, Iv, XX }, + { "pushP", ds, XX, XX }, + { "popP", ds, XX, XX }, + /* 20 */ + { "andB", Eb, Gb, XX }, + { "andS", Ev, Gv, XX }, + { "andB", Gb, Eb, XX }, + { "andS", Gv, Ev, XX }, + { "andB", AL, Ib, XX }, + { "andS", eAX, Iv, XX }, + { "(bad)", XX, XX, XX }, /* SEG ES prefix */ + { "daa", XX, XX, XX }, + /* 28 */ + { "subB", Eb, Gb, XX }, + { "subS", Ev, Gv, XX }, + { "subB", Gb, Eb, XX }, + { "subS", Gv, Ev, XX }, + { "subB", AL, Ib, XX }, + { "subS", eAX, Iv, XX }, + { "(bad)", XX, XX, XX }, /* SEG CS prefix */ + { "das", XX, XX, XX }, + /* 30 */ + { "xorB", Eb, Gb, XX }, + { "xorS", Ev, Gv, XX }, + { "xorB", Gb, Eb, XX }, + { "xorS", Gv, Ev, XX }, + { "xorB", AL, Ib, XX }, + { "xorS", eAX, Iv, XX }, + { "(bad)", XX, XX, XX }, /* SEG SS prefix */ + { "aaa", XX, XX, XX }, + /* 38 */ + { "cmpB", Eb, Gb, XX }, + { "cmpS", Ev, Gv, XX }, + { "cmpB", Gb, Eb, XX }, + { "cmpS", Gv, Ev, XX }, + { "cmpB", AL, Ib, XX }, + { "cmpS", eAX, Iv, XX }, + { "(bad)", XX, XX, XX }, /* SEG DS prefix */ + { "aas", XX, XX, XX }, + /* 40 */ + { "incS", eAX, XX, XX }, + { "incS", eCX, XX, XX }, + { "incS", eDX, XX, XX }, + { "incS", eBX, XX, XX }, + { "incS", eSP, XX, XX }, + { "incS", eBP, XX, XX }, + { "incS", eSI, XX, XX }, + { "incS", eDI, XX, XX }, + /* 48 */ + { "decS", eAX, XX, XX }, + { "decS", eCX, XX, XX }, + { "decS", eDX, XX, XX }, + { "decS", eBX, XX, XX }, + { "decS", eSP, XX, XX }, + { "decS", eBP, XX, XX }, + { "decS", eSI, XX, XX }, + { "decS", eDI, XX, XX }, + /* 50 */ + { "pushS", eAX, XX, XX }, + { "pushS", eCX, XX, XX }, + { "pushS", eDX, XX, XX }, + { "pushS", eBX, XX, XX }, + { "pushS", eSP, XX, XX }, + { "pushS", eBP, XX, XX }, + { "pushS", eSI, XX, XX }, + { "pushS", eDI, XX, XX }, + /* 58 */ + { "popS", eAX, XX, XX }, + { "popS", eCX, XX, XX }, + { "popS", eDX, XX, XX }, + { "popS", eBX, XX, XX }, + { "popS", eSP, XX, XX }, + { "popS", eBP, XX, XX }, + { "popS", eSI, XX, XX }, + { "popS", eDI, XX, XX }, + /* 60 */ + { "pushaP", XX, XX, XX }, + { "popaP", XX, XX, XX }, + { "boundS", Gv, Ma, XX }, + { "arpl", Ew, Gw, XX }, + { "(bad)", XX, XX, XX }, /* seg fs */ + { "(bad)", XX, XX, XX }, /* seg gs */ + { "(bad)", XX, XX, XX }, /* op size prefix */ + { "(bad)", XX, XX, XX }, /* adr size prefix */ + /* 68 */ + { "pushP", Iv, XX, XX }, /* 386 book wrong */ + { "imulS", Gv, Ev, Iv }, + { "pushP", sIb, XX, XX }, /* push of byte really pushes 2 or 4 bytes */ + { "imulS", Gv, Ev, sIb }, + { "insb", Yb, indirDX, XX }, + { "insR", Yv, indirDX, XX }, + { "outsb", indirDX, Xb, XX }, + { "outsR", indirDX, Xv, XX }, + /* 70 */ + { "jo", Jb, XX, XX }, + { "jno", Jb, XX, XX }, + { "jb", Jb, XX, XX }, + { "jae", Jb, XX, XX }, + { "je", Jb, XX, XX }, + { "jne", Jb, XX, XX }, + { "jbe", Jb, XX, XX }, + { "ja", Jb, XX, XX }, + /* 78 */ + { "js", Jb, XX, XX }, + { "jns", Jb, XX, XX }, + { "jp", Jb, XX, XX }, + { "jnp", Jb, XX, XX }, + { "jl", Jb, XX, XX }, + { "jge", Jb, XX, XX }, + { "jle", Jb, XX, XX }, + { "jg", Jb, XX, XX }, + /* 80 */ + { GRP1b }, + { GRP1S }, + { "(bad)", XX, XX, XX }, + { GRP1Ss }, + { "testB", Eb, Gb, XX }, + { "testS", Ev, Gv, XX }, + { "xchgB", Eb, Gb, XX }, + { "xchgS", Ev, Gv, XX }, + /* 88 */ + { "movB", Eb, Gb, XX }, + { "movS", Ev, Gv, XX }, + { "movB", Gb, Eb, XX }, + { "movS", Gv, Ev, XX }, + { "movQ", Ev, Sw, XX }, + { "leaS", Gv, M, XX }, + { "movQ", Sw, Ev, XX }, + { "popQ", Ev, XX, XX }, + /* 90 */ + { "nop", XX, XX, XX }, + { "xchgS", eCX, eAX, XX }, + { "xchgS", eDX, eAX, XX }, + { "xchgS", eBX, eAX, XX }, + { "xchgS", eSP, eAX, XX }, + { "xchgS", eBP, eAX, XX }, + { "xchgS", eSI, eAX, XX }, + { "xchgS", eDI, eAX, XX }, + /* 98 */ + { "cWtR", XX, XX, XX }, + { "cRtd", XX, XX, XX }, + { "lcallP", Ap, XX, XX }, + { "(bad)", XX, XX, XX }, /* fwait */ + { "pushfP", XX, XX, XX }, + { "popfP", XX, XX, XX }, + { "sahf", XX, XX, XX }, + { "lahf", XX, XX, XX }, + /* a0 */ + { "movB", AL, Ob, XX }, + { "movS", eAX, Ov, XX }, + { "movB", Ob, AL, XX }, + { "movS", Ov, eAX, XX }, + { "movsb", Yb, Xb, XX }, + { "movsR", Yv, Xv, XX }, + { "cmpsb", Xb, Yb, XX }, + { "cmpsR", Xv, Yv, XX }, + /* a8 */ + { "testB", AL, Ib, XX }, + { "testS", eAX, Iv, XX }, + { "stosB", Yb, AL, XX }, + { "stosS", Yv, eAX, XX }, + { "lodsB", AL, Xb, XX }, + { "lodsS", eAX, Xv, XX }, + { "scasB", AL, Yb, XX }, + { "scasS", eAX, Yv, XX }, + /* b0 */ + { "movB", AL, Ib, XX }, + { "movB", CL, Ib, XX }, + { "movB", DL, Ib, XX }, + { "movB", BL, Ib, XX }, + { "movB", AH, Ib, XX }, + { "movB", CH, Ib, XX }, + { "movB", DH, Ib, XX }, + { "movB", BH, Ib, XX }, + /* b8 */ + { "movS", eAX, Iv, XX }, + { "movS", eCX, Iv, XX }, + { "movS", eDX, Iv, XX }, + { "movS", eBX, Iv, XX }, + { "movS", eSP, Iv, XX }, + { "movS", eBP, Iv, XX }, + { "movS", eSI, Iv, XX }, + { "movS", eDI, Iv, XX }, + /* c0 */ + { GRP2b }, + { GRP2S }, + { "retP", Iw, XX, XX }, + { "retP", XX, XX, XX }, + { "lesS", Gv, Mp, XX }, + { "ldsS", Gv, Mp, XX }, + { "movA", Eb, Ib, XX }, + { "movQ", Ev, Iv, XX }, + /* c8 */ + { "enterP", Iw, Ib, XX }, + { "leaveP", XX, XX, XX }, + { "lretP", Iw, XX, XX }, + { "lretP", XX, XX, XX }, + { "int3", XX, XX, XX }, + { "int", Ib, XX, XX }, + { "into", XX, XX, XX}, + { "iretP", XX, XX, XX }, + /* d0 */ + { GRP2b_one }, + { GRP2S_one }, + { GRP2b_cl }, + { GRP2S_cl }, + { "aam", sIb, XX, XX }, + { "aad", sIb, XX, XX }, + { "(bad)", XX, XX, XX }, + { "xlat", DSBX, XX, XX }, + /* d8 */ + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + /* e0 */ + { "loopne", Jb, XX, XX }, + { "loope", Jb, XX, XX }, + { "loop", Jb, XX, XX }, + { "jEcxz", Jb, XX, XX }, + { "inB", AL, Ib, XX }, + { "inS", eAX, Ib, XX }, + { "outB", Ib, AL, XX }, + { "outS", Ib, eAX, XX }, + /* e8 */ + { "callP", Jv, XX, XX }, + { "jmpP", Jv, XX, XX }, + { "ljmpP", Ap, XX, XX }, + { "jmp", Jb, XX, XX }, + { "inB", AL, indirDX, XX }, + { "inS", eAX, indirDX, XX }, + { "outB", indirDX, AL, XX }, + { "outS", indirDX, eAX, XX }, + /* f0 */ + { "(bad)", XX, XX, XX }, /* lock prefix */ + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, /* repne */ + { "(bad)", XX, XX, XX }, /* repz */ + { "hlt", XX, XX, XX }, + { "cmc", XX, XX, XX }, + { GRP3b }, + { GRP3S }, + /* f8 */ + { "clc", XX, XX, XX }, + { "stc", XX, XX, XX }, + { "cli", XX, XX, XX }, + { "sti", XX, XX, XX }, + { "cld", XX, XX, XX }, + { "std", XX, XX, XX }, + { GRP4 }, + { GRP5 }, +}; + +static const struct dis386 dis386_intel[] = { + /* 00 */ + { "add", Eb, Gb, XX }, + { "add", Ev, Gv, XX }, + { "add", Gb, Eb, XX }, + { "add", Gv, Ev, XX }, + { "add", AL, Ib, XX }, + { "add", eAX, Iv, XX }, + { "push", es, XX, XX }, + { "pop", es, XX, XX }, + /* 08 */ + { "or", Eb, Gb, XX }, + { "or", Ev, Gv, XX }, + { "or", Gb, Eb, XX }, + { "or", Gv, Ev, XX }, + { "or", AL, Ib, XX }, + { "or", eAX, Iv, XX }, + { "push", cs, XX, XX }, + { "(bad)", XX, XX, XX }, /* 0x0f extended opcode escape */ + /* 10 */ + { "adc", Eb, Gb, XX }, + { "adc", Ev, Gv, XX }, + { "adc", Gb, Eb, XX }, + { "adc", Gv, Ev, XX }, + { "adc", AL, Ib, XX }, + { "adc", eAX, Iv, XX }, + { "push", ss, XX, XX }, + { "pop", ss, XX, XX }, + /* 18 */ + { "sbb", Eb, Gb, XX }, + { "sbb", Ev, Gv, XX }, + { "sbb", Gb, Eb, XX }, + { "sbb", Gv, Ev, XX }, + { "sbb", AL, Ib, XX }, + { "sbb", eAX, Iv, XX }, + { "push", ds, XX, XX }, + { "pop", ds, XX, XX }, + /* 20 */ + { "and", Eb, Gb, XX }, + { "and", Ev, Gv, XX }, + { "and", Gb, Eb, XX }, + { "and", Gv, Ev, XX }, + { "and", AL, Ib, XX }, + { "and", eAX, Iv, XX }, + { "(bad)", XX, XX, XX }, /* SEG ES prefix */ + { "daa", XX, XX, XX }, + /* 28 */ + { "sub", Eb, Gb, XX }, + { "sub", Ev, Gv, XX }, + { "sub", Gb, Eb, XX }, + { "sub", Gv, Ev, XX }, + { "sub", AL, Ib, XX }, + { "sub", eAX, Iv, XX }, + { "(bad)", XX, XX, XX }, /* SEG CS prefix */ + { "das", XX, XX, XX }, + /* 30 */ + { "xor", Eb, Gb, XX }, + { "xor", Ev, Gv, XX }, + { "xor", Gb, Eb, XX }, + { "xor", Gv, Ev, XX }, + { "xor", AL, Ib, XX }, + { "xor", eAX, Iv, XX }, + { "(bad)", XX, XX, XX }, /* SEG SS prefix */ + { "aaa", XX, XX, XX }, + /* 38 */ + { "cmp", Eb, Gb, XX }, + { "cmp", Ev, Gv, XX }, + { "cmp", Gb, Eb, XX }, + { "cmp", Gv, Ev, XX }, + { "cmp", AL, Ib, XX }, + { "cmp", eAX, Iv, XX }, + { "(bad)", XX, XX, XX }, /* SEG DS prefix */ + { "aas", XX, XX, XX }, + /* 40 */ + { "inc", eAX, XX, XX }, + { "inc", eCX, XX, XX }, + { "inc", eDX, XX, XX }, + { "inc", eBX, XX, XX }, + { "inc", eSP, XX, XX }, + { "inc", eBP, XX, XX }, + { "inc", eSI, XX, XX }, + { "inc", eDI, XX, XX }, + /* 48 */ + { "dec", eAX, XX, XX }, + { "dec", eCX, XX, XX }, + { "dec", eDX, XX, XX }, + { "dec", eBX, XX, XX }, + { "dec", eSP, XX, XX }, + { "dec", eBP, XX, XX }, + { "dec", eSI, XX, XX }, + { "dec", eDI, XX, XX }, + /* 50 */ + { "push", eAX, XX, XX }, + { "push", eCX, XX, XX }, + { "push", eDX, XX, XX }, + { "push", eBX, XX, XX }, + { "push", eSP, XX, XX }, + { "push", eBP, XX, XX }, + { "push", eSI, XX, XX }, + { "push", eDI, XX, XX }, + /* 58 */ + { "pop", eAX, XX, XX }, + { "pop", eCX, XX, XX }, + { "pop", eDX, XX, XX }, + { "pop", eBX, XX, XX }, + { "pop", eSP, XX, XX }, + { "pop", eBP, XX, XX }, + { "pop", eSI, XX, XX }, + { "pop", eDI, XX, XX }, + /* 60 */ + { "pusha", XX, XX, XX }, + { "popa", XX, XX, XX }, + { "bound", Gv, Ma, XX }, + { "arpl", Ew, Gw, XX }, + { "(bad)", XX, XX, XX }, /* seg fs */ + { "(bad)", XX, XX, XX }, /* seg gs */ + { "(bad)", XX, XX, XX }, /* op size prefix */ + { "(bad)", XX, XX, XX }, /* adr size prefix */ + /* 68 */ + { "push", Iv, XX, XX }, /* 386 book wrong */ + { "imul", Gv, Ev, Iv }, + { "push", sIb, XX, XX }, /* push of byte really pushes 2 or 4 bytes */ + { "imul", Gv, Ev, sIb }, + { "ins", Yb, indirDX, XX }, + { "ins", Yv, indirDX, XX }, + { "outs", indirDX, Xb, XX }, + { "outs", indirDX, Xv, XX }, + /* 70 */ + { "jo", Jb, XX, XX }, + { "jno", Jb, XX, XX }, + { "jb", Jb, XX, XX }, + { "jae", Jb, XX, XX }, + { "je", Jb, XX, XX }, + { "jne", Jb, XX, XX }, + { "jbe", Jb, XX, XX }, + { "ja", Jb, XX, XX }, + /* 78 */ + { "js", Jb, XX, XX }, + { "jns", Jb, XX, XX }, + { "jp", Jb, XX, XX }, + { "jnp", Jb, XX, XX }, + { "jl", Jb, XX, XX }, + { "jge", Jb, XX, XX }, + { "jle", Jb, XX, XX }, + { "jg", Jb, XX, XX }, + /* 80 */ + { GRP1b }, + { GRP1S }, + { "(bad)", XX, XX, XX }, + { GRP1Ss }, + { "test", Eb, Gb, XX }, + { "test", Ev, Gv, XX }, + { "xchg", Eb, Gb, XX }, + { "xchg", Ev, Gv, XX }, + /* 88 */ + { "mov", Eb, Gb, XX }, + { "mov", Ev, Gv, XX }, + { "mov", Gb, Eb, XX }, + { "mov", Gv, Ev, XX }, + { "mov", Ev, Sw, XX }, + { "lea", Gv, M, XX }, + { "mov", Sw, Ev, XX }, + { "pop", Ev, XX, XX }, + /* 90 */ + { "nop", XX, XX, XX }, + { "xchg", eCX, eAX, XX }, + { "xchg", eDX, eAX, XX }, + { "xchg", eBX, eAX, XX }, + { "xchg", eSP, eAX, XX }, + { "xchg", eBP, eAX, XX }, + { "xchg", eSI, eAX, XX }, + { "xchg", eDI, eAX, XX }, + /* 98 */ + { "cW", XX, XX, XX }, /* cwde and cbw */ + { "cR", XX, XX, XX }, /* cdq and cwd */ + { "lcall", Ap, XX, XX }, + { "(bad)", XX, XX, XX }, /* fwait */ + { "pushf", XX, XX, XX }, + { "popf", XX, XX, XX }, + { "sahf", XX, XX, XX }, + { "lahf", XX, XX, XX }, + /* a0 */ + { "mov", AL, Ob, XX }, + { "mov", eAX, Ov, XX }, + { "mov", Ob, AL, XX }, + { "mov", Ov, eAX, XX }, + { "movs", Yb, Xb, XX }, + { "movs", Yv, Xv, XX }, + { "cmps", Xb, Yb, XX }, + { "cmps", Xv, Yv, XX }, + /* a8 */ + { "test", AL, Ib, XX }, + { "test", eAX, Iv, XX }, + { "stos", Yb, AL, XX }, + { "stos", Yv, eAX, XX }, + { "lods", AL, Xb, XX }, + { "lods", eAX, Xv, XX }, + { "scas", AL, Yb, XX }, + { "scas", eAX, Yv, XX }, + /* b0 */ + { "mov", AL, Ib, XX }, + { "mov", CL, Ib, XX }, + { "mov", DL, Ib, XX }, + { "mov", BL, Ib, XX }, + { "mov", AH, Ib, XX }, + { "mov", CH, Ib, XX }, + { "mov", DH, Ib, XX }, + { "mov", BH, Ib, XX }, + /* b8 */ + { "mov", eAX, Iv, XX }, + { "mov", eCX, Iv, XX }, + { "mov", eDX, Iv, XX }, + { "mov", eBX, Iv, XX }, + { "mov", eSP, Iv, XX }, + { "mov", eBP, Iv, XX }, + { "mov", eSI, Iv, XX }, + { "mov", eDI, Iv, XX }, + /* c0 */ + { GRP2b }, + { GRP2S }, + { "ret", Iw, XX, XX }, + { "ret", XX, XX, XX }, + { "les", Gv, Mp, XX }, + { "lds", Gv, Mp, XX }, + { "mov", Eb, Ib, XX }, + { "mov", Ev, Iv, XX }, + /* c8 */ + { "enter", Iw, Ib, XX }, + { "leave", XX, XX, XX }, + { "lret", Iw, XX, XX }, + { "lret", XX, XX, XX }, + { "int3", XX, XX, XX }, + { "int", Ib, XX, XX }, + { "into", XX, XX, XX }, + { "iret", XX, XX, XX }, + /* d0 */ + { GRP2b_one }, + { GRP2S_one }, + { GRP2b_cl }, + { GRP2S_cl }, + { "aam", sIb, XX, XX }, + { "aad", sIb, XX, XX }, + { "(bad)", XX, XX, XX }, + { "xlat", DSBX, XX, XX }, + /* d8 */ + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + /* e0 */ + { "loopne", Jb, XX, XX }, + { "loope", Jb, XX, XX }, + { "loop", Jb, XX, XX }, + { "jEcxz", Jb, XX, XX }, + { "in", AL, Ib, XX }, + { "in", eAX, Ib, XX }, + { "out", Ib, AL, XX }, + { "out", Ib, eAX, XX }, + /* e8 */ + { "call", Jv, XX, XX }, + { "jmp", Jv, XX, XX }, + { "ljmp", Ap, XX, XX }, + { "jmp", Jb, XX, XX }, + { "in", AL, indirDX, XX }, + { "in", eAX, indirDX, XX }, + { "out", indirDX, AL, XX }, + { "out", indirDX, eAX, XX }, + /* f0 */ + { "(bad)", XX, XX, XX }, /* lock prefix */ + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, /* repne */ + { "(bad)", XX, XX, XX }, /* repz */ + { "hlt", XX, XX, XX }, + { "cmc", XX, XX, XX }, + { GRP3b }, + { GRP3S }, + /* f8 */ + { "clc", XX, XX, XX }, + { "stc", XX, XX, XX }, + { "cli", XX, XX, XX }, + { "sti", XX, XX, XX }, + { "cld", XX, XX, XX }, + { "std", XX, XX, XX }, + { GRP4 }, + { GRP5 }, +}; + +static const struct dis386 dis386_twobyte_att[] = { + /* 00 */ + { GRP6 }, + { GRP7 }, + { "larS", Gv, Ew, XX }, + { "lslS", Gv, Ew, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "clts", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + /* 08 */ + { "invd", XX, XX, XX }, + { "wbinvd", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "ud2a", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { GRPAMD }, + { "femms", XX, XX, XX }, + { "", MX, EM, OPSUF }, /* See OP_3DNowSuffix */ + /* 10 */ + { PREGRP8 }, + { PREGRP9 }, + { "movlps", XM, EX, SIMD_Fixup, 'h' }, /* really only 2 operands */ + { "movlps", EX, XM, SIMD_Fixup, 'h' }, + { "unpcklps", XM, EX, XX }, + { "unpckhps", XM, EX, XX }, + { "movhps", XM, EX, SIMD_Fixup, 'l' }, + { "movhps", EX, XM, SIMD_Fixup, 'l' }, + /* 18 */ + { GRP14 }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + /* 20 */ + /* these are all backward in appendix A of the intel book */ + { "movL", Rd, Cd, XX }, + { "movL", Rd, Dd, XX }, + { "movL", Cd, Rd, XX }, + { "movL", Dd, Rd, XX }, + { "movL", Rd, Td, XX }, + { "(bad)", XX, XX, XX }, + { "movL", Td, Rd, XX }, + { "(bad)", XX, XX, XX }, + /* 28 */ + { "movaps", XM, EX, XX }, + { "movaps", EX, XM, XX }, + { PREGRP2 }, + { "movntps", Ev, XM, XX }, + { PREGRP4 }, + { PREGRP3 }, + { "ucomiss", XM, EX, XX }, + { "comiss", XM, EX, XX }, + /* 30 */ + { "wrmsr", XX, XX, XX }, + { "rdtsc", XX, XX, XX }, + { "rdmsr", XX, XX, XX }, + { "rdpmc", XX, XX, XX }, + { "sysenter", XX, XX, XX }, + { "sysexit", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + /* 38 */ + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + /* 40 */ + { "cmovo", Gv, Ev, XX }, + { "cmovno", Gv, Ev, XX }, + { "cmovb", Gv, Ev, XX }, + { "cmovae", Gv, Ev, XX }, + { "cmove", Gv, Ev, XX }, + { "cmovne", Gv, Ev, XX }, + { "cmovbe", Gv, Ev, XX }, + { "cmova", Gv, Ev, XX }, + /* 48 */ + { "cmovs", Gv, Ev, XX }, + { "cmovns", Gv, Ev, XX }, + { "cmovp", Gv, Ev, XX }, + { "cmovnp", Gv, Ev, XX }, + { "cmovl", Gv, Ev, XX }, + { "cmovge", Gv, Ev, XX }, + { "cmovle", Gv, Ev, XX }, + { "cmovg", Gv, Ev, XX }, + /* 50 */ + { "movmskps", Gv, EX, XX }, + { PREGRP13 }, + { PREGRP12 }, + { PREGRP11 }, + { "andps", XM, EX, XX }, + { "andnps", XM, EX, XX }, + { "orps", XM, EX, XX }, + { "xorps", XM, EX, XX }, + /* 58 */ + { PREGRP0 }, + { PREGRP10 }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { PREGRP14 }, + { PREGRP7 }, + { PREGRP5 }, + { PREGRP6 }, + /* 60 */ + { "punpcklbw", MX, EM, XX }, + { "punpcklwd", MX, EM, XX }, + { "punpckldq", MX, EM, XX }, + { "packsswb", MX, EM, XX }, + { "pcmpgtb", MX, EM, XX }, + { "pcmpgtw", MX, EM, XX }, + { "pcmpgtd", MX, EM, XX }, + { "packuswb", MX, EM, XX }, + /* 68 */ + { "punpckhbw", MX, EM, XX }, + { "punpckhwd", MX, EM, XX }, + { "punpckhdq", MX, EM, XX }, + { "packssdw", MX, EM, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "movd", MX, Ed, XX }, + { "movq", MX, EM, XX }, + /* 70 */ + { "pshufw", MX, EM, Ib }, + { GRP10 }, + { GRP11 }, + { GRP12 }, + { "pcmpeqb", MX, EM, XX }, + { "pcmpeqw", MX, EM, XX }, + { "pcmpeqd", MX, EM, XX }, + { "emms", XX, XX, XX }, + /* 78 */ + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "movd", Ed, MX, XX }, + { "movq", EM, MX, XX }, + /* 80 */ + { "jo", Jv, XX, XX }, + { "jno", Jv, XX, XX }, + { "jb", Jv, XX, XX }, + { "jae", Jv, XX, XX }, + { "je", Jv, XX, XX }, + { "jne", Jv, XX, XX }, + { "jbe", Jv, XX, XX }, + { "ja", Jv, XX, XX }, + /* 88 */ + { "js", Jv, XX, XX }, + { "jns", Jv, XX, XX }, + { "jp", Jv, XX, XX }, + { "jnp", Jv, XX, XX }, + { "jl", Jv, XX, XX }, + { "jge", Jv, XX, XX }, + { "jle", Jv, XX, XX }, + { "jg", Jv, XX, XX }, + /* 90 */ + { "seto", Eb, XX, XX }, + { "setno", Eb, XX, XX }, + { "setb", Eb, XX, XX }, + { "setae", Eb, XX, XX }, + { "sete", Eb, XX, XX }, + { "setne", Eb, XX, XX }, + { "setbe", Eb, XX, XX }, + { "seta", Eb, XX, XX }, + /* 98 */ + { "sets", Eb, XX, XX }, + { "setns", Eb, XX, XX }, + { "setp", Eb, XX, XX }, + { "setnp", Eb, XX, XX }, + { "setl", Eb, XX, XX }, + { "setge", Eb, XX, XX }, + { "setle", Eb, XX, XX }, + { "setg", Eb, XX, XX }, + /* a0 */ + { "pushP", fs, XX, XX }, + { "popP", fs, XX, XX }, + { "cpuid", XX, XX, XX }, + { "btS", Ev, Gv, XX }, + { "shldS", Ev, Gv, Ib }, + { "shldS", Ev, Gv, CL }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + /* a8 */ + { "pushP", gs, XX, XX }, + { "popP", gs, XX, XX }, + { "rsm", XX, XX, XX }, + { "btsS", Ev, Gv, XX }, + { "shrdS", Ev, Gv, Ib }, + { "shrdS", Ev, Gv, CL }, + { GRP13 }, + { "imulS", Gv, Ev, XX }, + /* b0 */ + { "cmpxchgB", Eb, Gb, XX }, + { "cmpxchgS", Ev, Gv, XX }, + { "lssS", Gv, Mp, XX }, + { "btrS", Ev, Gv, XX }, + { "lfsS", Gv, Mp, XX }, + { "lgsS", Gv, Mp, XX }, + { "movzbR", Gv, Eb, XX }, + { "movzwR", Gv, Ew, XX }, /* yes, there really is movzww ! */ + /* b8 */ + { "(bad)", XX, XX, XX }, + { "ud2b", XX, XX, XX }, + { GRP8 }, + { "btcS", Ev, Gv, XX }, + { "bsfS", Gv, Ev, XX }, + { "bsrS", Gv, Ev, XX }, + { "movsbR", Gv, Eb, XX }, + { "movswR", Gv, Ew, XX }, /* yes, there really is movsww ! */ + /* c0 */ + { "xaddB", Eb, Gb, XX }, + { "xaddS", Ev, Gv, XX }, + { PREGRP1 }, + { "(bad)", XX, XX, XX }, + { "pinsrw", MX, Ev, Ib }, + { "pextrw", Ev, MX, Ib }, + { "shufps", XM, EX, Ib }, + { GRP9 }, + /* c8 */ + { "bswap", eAX, XX, XX }, /* bswap doesn't support 16 bit regs */ + { "bswap", eCX, XX, XX }, + { "bswap", eDX, XX, XX }, + { "bswap", eBX, XX, XX }, + { "bswap", eSP, XX, XX }, + { "bswap", eBP, XX, XX }, + { "bswap", eSI, XX, XX }, + { "bswap", eDI, XX, XX }, + /* d0 */ + { "(bad)", XX, XX, XX }, + { "psrlw", MX, EM, XX }, + { "psrld", MX, EM, XX }, + { "psrlq", MX, EM, XX }, + { "(bad)", XX, XX, XX }, + { "pmullw", MX, EM, XX }, + { "(bad)", XX, XX, XX }, + { "pmovmskb", Ev, MX, XX }, + /* d8 */ + { "psubusb", MX, EM, XX }, + { "psubusw", MX, EM, XX }, + { "pminub", MX, EM, XX }, + { "pand", MX, EM, XX }, + { "paddusb", MX, EM, XX }, + { "paddusw", MX, EM, XX }, + { "pmaxub", MX, EM, XX }, + { "pandn", MX, EM, XX }, + /* e0 */ + { "pavgb", MX, EM, XX }, + { "psraw", MX, EM, XX }, + { "psrad", MX, EM, XX }, + { "pavgw", MX, EM, XX }, + { "pmulhuw", MX, EM, XX }, + { "pmulhw", MX, EM, XX }, + { "(bad)", XX, XX, XX }, + { "movntq", Ev, MX, XX }, + /* e8 */ + { "psubsb", MX, EM, XX }, + { "psubsw", MX, EM, XX }, + { "pminsw", MX, EM, XX }, + { "por", MX, EM, XX }, + { "paddsb", MX, EM, XX }, + { "paddsw", MX, EM, XX }, + { "pmaxsw", MX, EM, XX }, + { "pxor", MX, EM, XX }, + /* f0 */ + { "(bad)", XX, XX, XX }, + { "psllw", MX, EM, XX }, + { "pslld", MX, EM, XX }, + { "psllq", MX, EM, XX }, + { "(bad)", XX, XX, XX }, + { "pmaddwd", MX, EM, XX }, + { "psadbw", MX, EM, XX }, + { "maskmovq", MX, EM, XX }, + /* f8 */ + { "psubb", MX, EM, XX }, + { "psubw", MX, EM, XX }, + { "psubd", MX, EM, XX }, + { "(bad)", XX, XX, XX }, + { "paddb", MX, EM, XX }, + { "paddw", MX, EM, XX }, + { "paddd", MX, EM, XX }, + { "(bad)", XX, XX, XX } +}; + +static const struct dis386 dis386_twobyte_intel[] = { + /* 00 */ + { GRP6 }, + { GRP7 }, + { "lar", Gv, Ew, XX }, + { "lsl", Gv, Ew, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "clts", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + /* 08 */ + { "invd", XX, XX, XX }, + { "wbinvd", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "ud2a", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { GRPAMD }, + { "femms" , XX, XX, XX}, + { "", MX, EM, OPSUF }, /* See OP_3DNowSuffix */ + /* 10 */ + { PREGRP8 }, + { PREGRP9 }, + { "movlps", XM, EX, SIMD_Fixup, 'h' }, /* really only 2 operands */ + { "movlps", EX, XM, SIMD_Fixup, 'h' }, + { "unpcklps", XM, EX, XX }, + { "unpckhps", XM, EX, XX }, + { "movhps", XM, EX, SIMD_Fixup, 'l' }, + { "movhps", EX, XM, SIMD_Fixup, 'l' }, + /* 18 */ + { GRP14 }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + /* 20 */ + /* these are all backward in appendix A of the intel book */ + { "mov", Rd, Cd, XX }, + { "mov", Rd, Dd, XX }, + { "mov", Cd, Rd, XX }, + { "mov", Dd, Rd, XX }, + { "mov", Rd, Td, XX }, + { "(bad)", XX, XX, XX }, + { "mov", Td, Rd, XX }, + { "(bad)", XX, XX, XX }, + /* 28 */ + { "movaps", XM, EX, XX }, + { "movaps", EX, XM, XX }, + { PREGRP2 }, + { "movntps", Ev, XM, XX }, + { PREGRP4 }, + { PREGRP3 }, + { "ucomiss", XM, EX, XX }, + { "comiss", XM, EX, XX }, + /* 30 */ + { "wrmsr", XX, XX, XX }, + { "rdtsc", XX, XX, XX }, + { "rdmsr", XX, XX, XX }, + { "rdpmc", XX, XX, XX }, + { "sysenter", XX, XX, XX }, + { "sysexit", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + /* 38 */ + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + /* 40 */ + { "cmovo", Gv, Ev, XX }, + { "cmovno", Gv, Ev, XX }, + { "cmovb", Gv, Ev, XX }, + { "cmovae", Gv, Ev, XX }, + { "cmove", Gv, Ev, XX }, + { "cmovne", Gv, Ev, XX }, + { "cmovbe", Gv, Ev, XX }, + { "cmova", Gv, Ev, XX }, + /* 48 */ + { "cmovs", Gv, Ev, XX }, + { "cmovns", Gv, Ev, XX }, + { "cmovp", Gv, Ev, XX }, + { "cmovnp", Gv, Ev, XX }, + { "cmovl", Gv, Ev, XX }, + { "cmovge", Gv, Ev, XX }, + { "cmovle", Gv, Ev, XX }, + { "cmovg", Gv, Ev, XX }, + /* 50 */ + { "movmskps", Gv, EX, XX }, + { PREGRP13 }, + { PREGRP12 }, + { PREGRP11 }, + { "andps", XM, EX, XX }, + { "andnps", XM, EX, XX }, + { "orps", XM, EX, XX }, + { "xorps", XM, EX, XX }, + /* 58 */ + { PREGRP0 }, + { PREGRP10 }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { PREGRP14 }, + { PREGRP7 }, + { PREGRP5 }, + { PREGRP6 }, + /* 60 */ + { "punpcklbw", MX, EM, XX }, + { "punpcklwd", MX, EM, XX }, + { "punpckldq", MX, EM, XX }, + { "packsswb", MX, EM, XX }, + { "pcmpgtb", MX, EM, XX }, + { "pcmpgtw", MX, EM, XX }, + { "pcmpgtd", MX, EM, XX }, + { "packuswb", MX, EM, XX }, + /* 68 */ + { "punpckhbw", MX, EM, XX }, + { "punpckhwd", MX, EM, XX }, + { "punpckhdq", MX, EM, XX }, + { "packssdw", MX, EM, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "movd", MX, Ed, XX }, + { "movq", MX, EM, XX }, + /* 70 */ + { "pshufw", MX, EM, Ib }, + { GRP10 }, + { GRP11 }, + { GRP12 }, + { "pcmpeqb", MX, EM, XX }, + { "pcmpeqw", MX, EM, XX }, + { "pcmpeqd", MX, EM, XX }, + { "emms", XX, XX, XX }, + /* 78 */ + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "movd", Ed, MX, XX }, + { "movq", EM, MX, XX }, + /* 80 */ + { "jo", Jv, XX, XX }, + { "jno", Jv, XX, XX }, + { "jb", Jv, XX, XX }, + { "jae", Jv, XX, XX }, + { "je", Jv, XX, XX }, + { "jne", Jv, XX, XX }, + { "jbe", Jv, XX, XX }, + { "ja", Jv, XX, XX }, + /* 88 */ + { "js", Jv, XX, XX }, + { "jns", Jv, XX, XX }, + { "jp", Jv, XX, XX }, + { "jnp", Jv, XX, XX }, + { "jl", Jv, XX, XX }, + { "jge", Jv, XX, XX }, + { "jle", Jv, XX, XX }, + { "jg", Jv, XX, XX }, + /* 90 */ + { "seto", Eb, XX, XX }, + { "setno", Eb, XX, XX }, + { "setb", Eb, XX, XX }, + { "setae", Eb, XX, XX }, + { "sete", Eb, XX, XX }, + { "setne", Eb, XX, XX }, + { "setbe", Eb, XX, XX }, + { "seta", Eb, XX, XX }, + /* 98 */ + { "sets", Eb, XX, XX }, + { "setns", Eb, XX, XX }, + { "setp", Eb, XX, XX }, + { "setnp", Eb, XX, XX }, + { "setl", Eb, XX, XX }, + { "setge", Eb, XX, XX }, + { "setle", Eb, XX, XX }, + { "setg", Eb, XX, XX }, + /* a0 */ + { "push", fs, XX, XX }, + { "pop", fs, XX, XX }, + { "cpuid", XX, XX, XX }, + { "bt", Ev, Gv, XX }, + { "shld", Ev, Gv, Ib }, + { "shld", Ev, Gv, CL }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + /* a8 */ + { "push", gs, XX, XX }, + { "pop", gs, XX, XX }, + { "rsm" , XX, XX, XX}, + { "bts", Ev, Gv, XX }, + { "shrd", Ev, Gv, Ib }, + { "shrd", Ev, Gv, CL }, + { GRP13 }, + { "imul", Gv, Ev, XX }, + /* b0 */ + { "cmpxchg", Eb, Gb, XX }, + { "cmpxchg", Ev, Gv, XX }, + { "lss", Gv, Mp, XX }, + { "btr", Ev, Gv, XX }, + { "lfs", Gv, Mp, XX }, + { "lgs", Gv, Mp, XX }, + { "movzx", Gv, Eb, XX }, + { "movzx", Gv, Ew, XX }, + /* b8 */ + { "(bad)", XX, XX, XX }, + { "ud2b", XX, XX, XX }, + { GRP8 }, + { "btc", Ev, Gv, XX }, + { "bsf", Gv, Ev, XX }, + { "bsr", Gv, Ev, XX }, + { "movsx", Gv, Eb, XX }, + { "movsx", Gv, Ew, XX }, + /* c0 */ + { "xadd", Eb, Gb, XX }, + { "xadd", Ev, Gv, XX }, + { PREGRP1 }, + { "(bad)", XX, XX, XX }, + { "pinsrw", MX, Ev, Ib }, + { "pextrw", Ev, MX, Ib }, + { "shufps", XM, EX, Ib }, + { GRP9 }, + /* c8 */ + { "bswap", eAX, XX, XX }, /* bswap doesn't support 16 bit regs */ + { "bswap", eCX, XX, XX }, + { "bswap", eDX, XX, XX }, + { "bswap", eBX, XX, XX }, + { "bswap", eSP, XX, XX }, + { "bswap", eBP, XX, XX }, + { "bswap", eSI, XX, XX }, + { "bswap", eDI, XX, XX }, + /* d0 */ + { "(bad)", XX, XX, XX }, + { "psrlw", MX, EM, XX }, + { "psrld", MX, EM, XX }, + { "psrlq", MX, EM, XX }, + { "(bad)", XX, XX, XX }, + { "pmullw", MX, EM, XX }, + { "(bad)", XX, XX, XX }, + { "pmovmskb", Ev, MX, XX }, + /* d8 */ + { "psubusb", MX, EM, XX }, + { "psubusw", MX, EM, XX }, + { "pminub", MX, EM, XX }, + { "pand", MX, EM, XX }, + { "paddusb", MX, EM, XX }, + { "paddusw", MX, EM, XX }, + { "pmaxub", MX, EM, XX }, + { "pandn", MX, EM, XX }, + /* e0 */ + { "pavgb", MX, EM, XX }, + { "psraw", MX, EM, XX }, + { "psrad", MX, EM, XX }, + { "pavgw", MX, EM, XX }, + { "pmulhuw", MX, EM, XX }, + { "pmulhw", MX, EM, XX }, + { "(bad)", XX, XX, XX }, + { "movntq", Ev, MX, XX }, + /* e8 */ + { "psubsb", MX, EM, XX }, + { "psubsw", MX, EM, XX }, + { "pminsw", MX, EM, XX }, + { "por", MX, EM, XX }, + { "paddsb", MX, EM, XX }, + { "paddsw", MX, EM, XX }, + { "pmaxsw", MX, EM, XX }, + { "pxor", MX, EM, XX }, + /* f0 */ + { "(bad)", XX, XX, XX }, + { "psllw", MX, EM, XX }, + { "pslld", MX, EM, XX }, + { "psllq", MX, EM, XX }, + { "(bad)", XX, XX, XX }, + { "pmaddwd", MX, EM, XX }, + { "psadbw", MX, EM, XX }, + { "maskmovq", MX, EM, XX }, + /* f8 */ + { "psubb", MX, EM, XX }, + { "psubw", MX, EM, XX }, + { "psubd", MX, EM, XX }, + { "(bad)", XX, XX, XX }, + { "paddb", MX, EM, XX }, + { "paddw", MX, EM, XX }, + { "paddd", MX, EM, XX }, + { "(bad)", XX, XX, XX } +}; + +static const unsigned char onebyte_has_modrm[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 00 */ + /* 10 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 10 */ + /* 20 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 20 */ + /* 30 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 30 */ + /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40 */ + /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 50 */ + /* 60 */ 0,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0, /* 60 */ + /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 70 */ + /* 80 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 80 */ + /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 90 */ + /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* a0 */ + /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* b0 */ + /* c0 */ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* c0 */ + /* d0 */ 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* d0 */ + /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* e0 */ + /* f0 */ 0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1 /* f0 */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +static const unsigned char twobyte_has_modrm[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,1, /* 0f */ + /* 10 */ 1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, /* 1f */ + /* 20 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 2f */ + /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ + /* 40 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4f */ + /* 50 */ 1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1, /* 5f */ + /* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1, /* 6f */ + /* 70 */ 1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 9f */ + /* a0 */ 0,0,0,1,1,1,1,1,0,0,0,1,1,1,1,1, /* af */ + /* b0 */ 1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1, /* bf */ + /* c0 */ 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1, /* df */ + /* e0 */ 1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1, /* ef */ + /* f0 */ 0,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0 /* ff */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +static const unsigned char twobyte_uses_f3_prefix[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ------------------------------- */ + /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */ + /* 10 */ 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */ + /* 20 */ 0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0, /* 2f */ + /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ + /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */ + /* 50 */ 0,1,1,1,0,0,0,0,1,1,0,0,1,1,1,1, /* 5f */ + /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 6f */ + /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */ + /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */ + /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */ + /* c0 */ 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* df */ + /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* ef */ + /* f0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* ff */ + /* ------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +static char obuf[100]; +static char *obufp; +static char scratchbuf[100]; +static unsigned char *start_codep; +static unsigned char *insn_codep; +static unsigned char *codep; +static disassemble_info *the_info; +static int mod; +static int rm; +static int reg; +static void oappend PARAMS ((const char *s)); + +static const char *names32[]={ + "%eax","%ecx","%edx","%ebx", "%esp","%ebp","%esi","%edi", +}; +static const char *names16[] = { + "%ax","%cx","%dx","%bx","%sp","%bp","%si","%di", +}; +static const char *names8[] = { + "%al","%cl","%dl","%bl","%ah","%ch","%dh","%bh", +}; +static const char *names_seg[] = { + "%es","%cs","%ss","%ds","%fs","%gs","%?","%?", +}; +static const char *index16[] = { + "%bx,%si","%bx,%di","%bp,%si","%bp,%di","%si","%di","%bp","%bx" +}; + +static const struct dis386 grps[][8] = { + /* GRP1b */ + { + { "addA", Eb, Ib, XX }, + { "orA", Eb, Ib, XX }, + { "adcA", Eb, Ib, XX }, + { "sbbA", Eb, Ib, XX }, + { "andA", Eb, Ib, XX }, + { "subA", Eb, Ib, XX }, + { "xorA", Eb, Ib, XX }, + { "cmpA", Eb, Ib, XX } + }, + /* GRP1S */ + { + { "addQ", Ev, Iv, XX }, + { "orQ", Ev, Iv, XX }, + { "adcQ", Ev, Iv, XX }, + { "sbbQ", Ev, Iv, XX }, + { "andQ", Ev, Iv, XX }, + { "subQ", Ev, Iv, XX }, + { "xorQ", Ev, Iv, XX }, + { "cmpQ", Ev, Iv, XX } + }, + /* GRP1Ss */ + { + { "addQ", Ev, sIb, XX }, + { "orQ", Ev, sIb, XX }, + { "adcQ", Ev, sIb, XX }, + { "sbbQ", Ev, sIb, XX }, + { "andQ", Ev, sIb, XX }, + { "subQ", Ev, sIb, XX }, + { "xorQ", Ev, sIb, XX }, + { "cmpQ", Ev, sIb, XX } + }, + /* GRP2b */ + { + { "rolA", Eb, Ib, XX }, + { "rorA", Eb, Ib, XX }, + { "rclA", Eb, Ib, XX }, + { "rcrA", Eb, Ib, XX }, + { "shlA", Eb, Ib, XX }, + { "shrA", Eb, Ib, XX }, + { "(bad)", XX, XX, XX }, + { "sarA", Eb, Ib, XX }, + }, + /* GRP2S */ + { + { "rolQ", Ev, Ib, XX }, + { "rorQ", Ev, Ib, XX }, + { "rclQ", Ev, Ib, XX }, + { "rcrQ", Ev, Ib, XX }, + { "shlQ", Ev, Ib, XX }, + { "shrQ", Ev, Ib, XX }, + { "(bad)", XX, XX, XX }, + { "sarQ", Ev, Ib, XX }, + }, + /* GRP2b_one */ + { + { "rolA", Eb, XX, XX }, + { "rorA", Eb, XX, XX }, + { "rclA", Eb, XX, XX }, + { "rcrA", Eb, XX, XX }, + { "shlA", Eb, XX, XX }, + { "shrA", Eb, XX, XX }, + { "(bad)", XX, XX, XX }, + { "sarA", Eb, XX, XX }, + }, + /* GRP2S_one */ + { + { "rolQ", Ev, XX, XX }, + { "rorQ", Ev, XX, XX }, + { "rclQ", Ev, XX, XX }, + { "rcrQ", Ev, XX, XX }, + { "shlQ", Ev, XX, XX }, + { "shrQ", Ev, XX, XX }, + { "(bad)", XX, XX, XX}, + { "sarQ", Ev, XX, XX }, + }, + /* GRP2b_cl */ + { + { "rolA", Eb, CL, XX }, + { "rorA", Eb, CL, XX }, + { "rclA", Eb, CL, XX }, + { "rcrA", Eb, CL, XX }, + { "shlA", Eb, CL, XX }, + { "shrA", Eb, CL, XX }, + { "(bad)", XX, XX, XX }, + { "sarA", Eb, CL, XX }, + }, + /* GRP2S_cl */ + { + { "rolQ", Ev, CL, XX }, + { "rorQ", Ev, CL, XX }, + { "rclQ", Ev, CL, XX }, + { "rcrQ", Ev, CL, XX }, + { "shlQ", Ev, CL, XX }, + { "shrQ", Ev, CL, XX }, + { "(bad)", XX, XX, XX }, + { "sarQ", Ev, CL, XX } + }, + /* GRP3b */ + { + { "testA", Eb, Ib, XX }, + { "(bad)", Eb, XX, XX }, + { "notA", Eb, XX, XX }, + { "negA", Eb, XX, XX }, + { "mulB", AL, Eb, XX }, + { "imulB", AL, Eb, XX }, + { "divB", AL, Eb, XX }, + { "idivB", AL, Eb, XX } + }, + /* GRP3S */ + { + { "testQ", Ev, Iv, XX }, + { "(bad)", XX, XX, XX }, + { "notQ", Ev, XX, XX }, + { "negQ", Ev, XX, XX }, + { "mulS", eAX, Ev, XX }, + { "imulS", eAX, Ev, XX }, + { "divS", eAX, Ev, XX }, + { "idivS", eAX, Ev, XX }, + }, + /* GRP4 */ + { + { "incA", Eb, XX, XX }, + { "decA", Eb, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + }, + /* GRP5 */ + { + { "incQ", Ev, XX, XX }, + { "decQ", Ev, XX, XX }, + { "callP", indirEv, XX, XX }, + { "lcallP", indirEv, XX, XX }, + { "jmpP", indirEv, XX, XX }, + { "ljmpP", indirEv, XX, XX }, + { "pushQ", Ev, XX, XX }, + { "(bad)", XX, XX, XX }, + }, + /* GRP6 */ + { + { "sldt", Ew, XX, XX }, + { "str", Ew, XX, XX }, + { "lldt", Ew, XX, XX }, + { "ltr", Ew, XX, XX }, + { "verr", Ew, XX, XX }, + { "verw", Ew, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX } + }, + /* GRP7 */ + { + { "sgdt", Ew, XX, XX }, + { "sidt", Ew, XX, XX }, + { "lgdt", Ew, XX, XX }, + { "lidt", Ew, XX, XX }, + { "smsw", Ew, XX, XX }, + { "(bad)", XX, XX, XX }, + { "lmsw", Ew, XX, XX }, + { "invlpg", Ew, XX, XX }, + }, + /* GRP8 */ + { + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "btQ", Ev, Ib, XX }, + { "btsQ", Ev, Ib, XX }, + { "btrQ", Ev, Ib, XX }, + { "btcQ", Ev, Ib, XX }, + }, + /* GRP9 */ + { + { "(bad)", XX, XX, XX }, + { "cmpxchg8b", Ev, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + }, + /* GRP10 */ + { + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "psrlw", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + { "psraw", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + { "psllw", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + }, + /* GRP11 */ + { + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "psrld", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + { "psrad", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + { "pslld", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + }, + /* GRP12 */ + { + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "psrlq", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "psllq", MS, Ib, XX }, + { "(bad)", XX, XX, XX }, + }, + /* GRP13 */ + { + { "fxsave", Ev, XX, XX }, + { "fxrstor", Ev, XX, XX }, + { "ldmxcsr", Ev, XX, XX }, + { "stmxcsr", Ev, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "sfence", None, XX, XX }, + }, + /* GRP14 */ + { + { "prefetchnta", Ev, XX, XX }, + { "prefetcht0", Ev, XX, XX }, + { "prefetcht1", Ev, XX, XX }, + { "prefetcht2", Ev, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + }, + /* GRPAMD */ + { + { "prefetch", Eb, XX, XX }, + { "prefetchw", Eb, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + } + +}; + +static const struct dis386 prefix_user_table[][2] = { + /* PREGRP0 */ + { + { "addps", XM, EX, XX }, + { "addss", XM, EX, XX }, + }, + /* PREGRP1 */ + { + { "", XM, EX, OPSIMD }, /* See OP_SIMD_SUFFIX */ + { "", XM, EX, OPSIMD }, + }, + /* PREGRP2 */ + { + { "cvtpi2ps", XM, EM, XX }, + { "cvtsi2ss", XM, Ev, XX }, + }, + /* PREGRP3 */ + { + { "cvtps2pi", MX, EX, XX }, + { "cvtss2si", Gv, EX, XX }, + }, + /* PREGRP4 */ + { + { "cvttps2pi", MX, EX, XX }, + { "cvttss2si", Gv, EX, XX }, + }, + /* PREGRP5 */ + { + { "divps", XM, EX, XX }, + { "divss", XM, EX, XX }, + }, + /* PREGRP6 */ + { + { "maxps", XM, EX, XX }, + { "maxss", XM, EX, XX }, + }, + /* PREGRP7 */ + { + { "minps", XM, EX, XX }, + { "minss", XM, EX, XX }, + }, + /* PREGRP8 */ + { + { "movups", XM, EX, XX }, + { "movss", XM, EX, XX }, + }, + /* PREGRP9 */ + { + { "movups", EX, XM, XX }, + { "movss", EX, XM, XX }, + }, + /* PREGRP10 */ + { + { "mulps", XM, EX, XX }, + { "mulss", XM, EX, XX }, + }, + /* PREGRP11 */ + { + { "rcpps", XM, EX, XX }, + { "rcpss", XM, EX, XX }, + }, + /* PREGRP12 */ + { + { "rsqrtps", XM, EX, XX }, + { "rsqrtss", XM, EX, XX }, + }, + /* PREGRP13 */ + { + { "sqrtps", XM, EX, XX }, + { "sqrtss", XM, EX, XX }, + }, + /* PREGRP14 */ + { + { "subps", XM, EX, XX }, + { "subss", XM, EX, XX }, + } +}; + +#define INTERNAL_DISASSEMBLER_ERROR "" + +static void +ckprefix () +{ + prefixes = 0; + used_prefixes = 0; + while (1) + { + FETCH_DATA (the_info, codep + 1); + switch (*codep) + { + case 0xf3: + prefixes |= PREFIX_REPZ; + break; + case 0xf2: + prefixes |= PREFIX_REPNZ; + break; + case 0xf0: + prefixes |= PREFIX_LOCK; + break; + case 0x2e: + prefixes |= PREFIX_CS; + break; + case 0x36: + prefixes |= PREFIX_SS; + break; + case 0x3e: + prefixes |= PREFIX_DS; + break; + case 0x26: + prefixes |= PREFIX_ES; + break; + case 0x64: + prefixes |= PREFIX_FS; + break; + case 0x65: + prefixes |= PREFIX_GS; + break; + case 0x66: + prefixes |= PREFIX_DATA; + break; + case 0x67: + prefixes |= PREFIX_ADDR; + break; + case FWAIT_OPCODE: + /* fwait is really an instruction. If there are prefixes + before the fwait, they belong to the fwait, *not* to the + following instruction. */ + if (prefixes) + { + prefixes |= PREFIX_FWAIT; + codep++; + return; + } + prefixes = PREFIX_FWAIT; + break; + default: + return; + } + codep++; + } +} + +/* Return the name of the prefix byte PREF, or NULL if PREF is not a + prefix byte. */ + +static const char * +prefix_name (pref, sizeflag) + int pref; + int sizeflag; +{ + switch (pref) + { + case 0xf3: + return "repz"; + case 0xf2: + return "repnz"; + case 0xf0: + return "lock"; + case 0x2e: + return "cs"; + case 0x36: + return "ss"; + case 0x3e: + return "ds"; + case 0x26: + return "es"; + case 0x64: + return "fs"; + case 0x65: + return "gs"; + case 0x66: + return (sizeflag & DFLAG) ? "data16" : "data32"; + case 0x67: + return (sizeflag & AFLAG) ? "addr16" : "addr32"; + case FWAIT_OPCODE: + return "fwait"; + default: + return NULL; + } +} + +static char op1out[100], op2out[100], op3out[100]; +static int op_ad, op_index[3]; +static unsigned int op_address[3]; +static unsigned int start_pc; + + +/* + * On the 386's of 1988, the maximum length of an instruction is 15 bytes. + * (see topic "Redundant prefixes" in the "Differences from 8086" + * section of the "Virtual 8086 Mode" chapter.) + * 'pc' should be the address of this instruction, it will + * be used to print the target address if this is a relative jump or call + * The function returns the length of this instruction in bytes. + */ + +static int print_insn_i386 + PARAMS ((bfd_vma pc, disassemble_info *info)); + +static char intel_syntax; +static char open_char; +static char close_char; +static char separator_char; +static char scale_char; + +int +print_insn_i386_att (pc, info) + bfd_vma pc; + disassemble_info *info; +{ + intel_syntax = 0; + open_char = '('; + close_char = ')'; + separator_char = ','; + scale_char = ','; + + return print_insn_i386 (pc, info); +} + +int +print_insn_i386_intel (pc, info) + bfd_vma pc; + disassemble_info *info; +{ + intel_syntax = 1; + open_char = '['; + close_char = ']'; + separator_char = '+'; + scale_char = '*'; + + return print_insn_i386 (pc, info); +} + +static int +print_insn_i386 (pc, info) + bfd_vma pc; + disassemble_info *info; +{ + const struct dis386 *dp; + int i; + int two_source_ops; + char *first, *second, *third; + int needcomma; + unsigned char need_modrm; + unsigned char uses_f3_prefix; + VOLATILE int sizeflag; + VOLATILE int orig_sizeflag; + + struct dis_private priv; + bfd_byte *inbuf = priv.the_buffer; + + if (info->mach == bfd_mach_i386_i386 + || info->mach == bfd_mach_i386_i386_intel_syntax) + sizeflag = AFLAG|DFLAG; + else if (info->mach == bfd_mach_i386_i8086) + sizeflag = 0; + else + abort (); + orig_sizeflag = sizeflag; + + /* The output looks better if we put 7 bytes on a line, since that + puts most long word instructions on a single line. */ + info->bytes_per_line = 7; + + info->private_data = (PTR) &priv; + priv.max_fetched = priv.the_buffer; + priv.insn_start = pc; + + obuf[0] = 0; + op1out[0] = 0; + op2out[0] = 0; + op3out[0] = 0; + + op_index[0] = op_index[1] = op_index[2] = -1; + + the_info = info; + start_pc = pc; + start_codep = inbuf; + codep = inbuf; + +#ifndef __KERNEL__ + if (setjmp (priv.bailout) != 0) + { + const char *name; + + /* Getting here means we tried for data but didn't get it. That + means we have an incomplete instruction of some sort. Just + print the first byte as a prefix or a .byte pseudo-op. */ + if (codep > inbuf) + { + name = prefix_name (inbuf[0], orig_sizeflag); + if (name != NULL) + (*info->fprintf_func) (info->stream, "%s", name); + else + { + /* Just print the first byte as a .byte instruction. */ + (*info->fprintf_func) (info->stream, ".byte 0x%x", + (unsigned int) inbuf[0]); + } + + return 1; + } + + return -1; + } +#endif + + ckprefix (); + + insn_codep = codep; + + FETCH_DATA (info, codep + 1); + two_source_ops = (*codep == 0x62) || (*codep == 0xc8); + + obufp = obuf; + + if ((prefixes & PREFIX_FWAIT) + && ((*codep < 0xd8) || (*codep > 0xdf))) + { + const char *name; + + /* fwait not followed by floating point instruction. Print the + first prefix, which is probably fwait itself. */ + name = prefix_name (inbuf[0], orig_sizeflag); + if (name == NULL) + name = INTERNAL_DISASSEMBLER_ERROR; + (*info->fprintf_func) (info->stream, "%s", name); + return 1; + } + + if (*codep == 0x0f) + { + FETCH_DATA (info, codep + 2); + if (intel_syntax) + dp = &dis386_twobyte_intel[*++codep]; + else + dp = &dis386_twobyte_att[*++codep]; + need_modrm = twobyte_has_modrm[*codep]; + uses_f3_prefix = twobyte_uses_f3_prefix[*codep]; + } + else + { + if (intel_syntax) + dp = &dis386_intel[*codep]; + else + dp = &dis386_att[*codep]; + need_modrm = onebyte_has_modrm[*codep]; + uses_f3_prefix = 0; + } + codep++; + + if (!uses_f3_prefix && (prefixes & PREFIX_REPZ)) + { + oappend ("repz "); + used_prefixes |= PREFIX_REPZ; + } + if (prefixes & PREFIX_REPNZ) + { + oappend ("repnz "); + used_prefixes |= PREFIX_REPNZ; + } + if (prefixes & PREFIX_LOCK) + { + oappend ("lock "); + used_prefixes |= PREFIX_LOCK; + } + + if (prefixes & PREFIX_DATA) + sizeflag ^= DFLAG; + + if (prefixes & PREFIX_ADDR) + { + sizeflag ^= AFLAG; + if (sizeflag & AFLAG) + oappend ("addr32 "); + else + oappend ("addr16 "); + used_prefixes |= PREFIX_ADDR; + } + + if (need_modrm) + { + FETCH_DATA (info, codep + 1); + mod = (*codep >> 6) & 3; + reg = (*codep >> 3) & 7; + rm = *codep & 7; + } + + if (dp->name == NULL && dp->bytemode1 == FLOATCODE) + { + dofloat (sizeflag); + } + else + { + if (dp->name == NULL) + { + switch(dp->bytemode2) + { + case USE_GROUPS: + dp = &grps[dp->bytemode1][reg]; + break; + case USE_PREFIX_USER_TABLE: + dp = &prefix_user_table[dp->bytemode1][prefixes & PREFIX_REPZ ? 1 : 0]; + used_prefixes |= (prefixes & PREFIX_REPZ); + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + break; + } + } + + putop (dp->name, sizeflag); + + obufp = op1out; + op_ad = 2; + if (dp->op1) + (*dp->op1)(dp->bytemode1, sizeflag); + + obufp = op2out; + op_ad = 1; + if (dp->op2) + (*dp->op2)(dp->bytemode2, sizeflag); + + obufp = op3out; + op_ad = 0; + if (dp->op3) + (*dp->op3)(dp->bytemode3, sizeflag); + } + + /* See if any prefixes were not used. If so, print the first one + separately. If we don't do this, we'll wind up printing an + instruction stream which does not precisely correspond to the + bytes we are disassembling. */ + if ((prefixes & ~used_prefixes) != 0) + { + const char *name; + + name = prefix_name (inbuf[0], orig_sizeflag); + if (name == NULL) + name = INTERNAL_DISASSEMBLER_ERROR; + (*info->fprintf_func) (info->stream, "%s", name); + return 1; + } + + obufp = obuf + strlen (obuf); + for (i = strlen (obuf); i < 6; i++) + oappend (" "); + oappend (" "); + (*info->fprintf_func) (info->stream, "%s", obuf); + + /* The enter and bound instructions are printed with operands in the same + order as the intel book; everything else is printed in reverse order. */ + if (intel_syntax || two_source_ops) + { + first = op1out; + second = op2out; + third = op3out; + op_ad = op_index[0]; + op_index[0] = op_index[2]; + op_index[2] = op_ad; + } + else + { + first = op3out; + second = op2out; + third = op1out; + } + needcomma = 0; + if (*first) + { + if (op_index[0] != -1) + (*info->print_address_func) ((bfd_vma) op_address[op_index[0]], info); + else + (*info->fprintf_func) (info->stream, "%s", first); + needcomma = 1; + } + if (*second) + { + if (needcomma) + (*info->fprintf_func) (info->stream, ","); + if (op_index[1] != -1) + (*info->print_address_func) ((bfd_vma) op_address[op_index[1]], info); + else + (*info->fprintf_func) (info->stream, "%s", second); + needcomma = 1; + } + if (*third) + { + if (needcomma) + (*info->fprintf_func) (info->stream, ","); + if (op_index[2] != -1) + (*info->print_address_func) ((bfd_vma) op_address[op_index[2]], info); + else + (*info->fprintf_func) (info->stream, "%s", third); + } + return codep - inbuf; +} + +static const char *float_mem_att[] = { + /* d8 */ + "fadds", + "fmuls", + "fcoms", + "fcomps", + "fsubs", + "fsubrs", + "fdivs", + "fdivrs", + /* d9 */ + "flds", + "(bad)", + "fsts", + "fstps", + "fldenv", + "fldcw", + "fNstenv", + "fNstcw", + /* da */ + "fiaddl", + "fimull", + "ficoml", + "ficompl", + "fisubl", + "fisubrl", + "fidivl", + "fidivrl", + /* db */ + "fildl", + "(bad)", + "fistl", + "fistpl", + "(bad)", + "fldt", + "(bad)", + "fstpt", + /* dc */ + "faddl", + "fmull", + "fcoml", + "fcompl", + "fsubl", + "fsubrl", + "fdivl", + "fdivrl", + /* dd */ + "fldl", + "(bad)", + "fstl", + "fstpl", + "frstor", + "(bad)", + "fNsave", + "fNstsw", + /* de */ + "fiadd", + "fimul", + "ficom", + "ficomp", + "fisub", + "fisubr", + "fidiv", + "fidivr", + /* df */ + "fild", + "(bad)", + "fist", + "fistp", + "fbld", + "fildll", + "fbstp", + "fistpll", +}; + +static const char *float_mem_intel[] = { + /* d8 */ + "fadd", + "fmul", + "fcom", + "fcomp", + "fsub", + "fsubr", + "fdiv", + "fdivr", + /* d9 */ + "fld", + "(bad)", + "fst", + "fstp", + "fldenv", + "fldcw", + "fNstenv", + "fNstcw", + /* da */ + "fiadd", + "fimul", + "ficom", + "ficomp", + "fisub", + "fisubr", + "fidiv", + "fidivr", + /* db */ + "fild", + "(bad)", + "fist", + "fistp", + "(bad)", + "fld", + "(bad)", + "fstp", + /* dc */ + "fadd", + "fmul", + "fcom", + "fcomp", + "fsub", + "fsubr", + "fdiv", + "fdivr", + /* dd */ + "fld", + "(bad)", + "fst", + "fstp", + "frstor", + "(bad)", + "fNsave", + "fNstsw", + /* de */ + "fiadd", + "fimul", + "ficom", + "ficomp", + "fisub", + "fisubr", + "fidiv", + "fidivr", + /* df */ + "fild", + "(bad)", + "fist", + "fistp", + "fbld", + "fild", + "fbstp", + "fistpll", +}; + +#define ST OP_ST, 0 +#define STi OP_STi, 0 + +#define FGRPd9_2 NULL, NULL, 0, NULL, 0, NULL, 0 +#define FGRPd9_4 NULL, NULL, 1, NULL, 0, NULL, 0 +#define FGRPd9_5 NULL, NULL, 2, NULL, 0, NULL, 0 +#define FGRPd9_6 NULL, NULL, 3, NULL, 0, NULL, 0 +#define FGRPd9_7 NULL, NULL, 4, NULL, 0, NULL, 0 +#define FGRPda_5 NULL, NULL, 5, NULL, 0, NULL, 0 +#define FGRPdb_4 NULL, NULL, 6, NULL, 0, NULL, 0 +#define FGRPde_3 NULL, NULL, 7, NULL, 0, NULL, 0 +#define FGRPdf_4 NULL, NULL, 8, NULL, 0, NULL, 0 + +static const struct dis386 float_reg[][8] = { + /* d8 */ + { + { "fadd", ST, STi, XX }, + { "fmul", ST, STi, XX }, + { "fcom", STi, XX, XX }, + { "fcomp", STi, XX, XX }, + { "fsub", ST, STi, XX }, + { "fsubr", ST, STi, XX }, + { "fdiv", ST, STi, XX }, + { "fdivr", ST, STi, XX }, + }, + /* d9 */ + { + { "fld", STi, XX, XX }, + { "fxch", STi, XX, XX }, + { FGRPd9_2 }, + { "(bad)", XX, XX, XX }, + { FGRPd9_4 }, + { FGRPd9_5 }, + { FGRPd9_6 }, + { FGRPd9_7 }, + }, + /* da */ + { + { "fcmovb", ST, STi, XX }, + { "fcmove", ST, STi, XX }, + { "fcmovbe",ST, STi, XX }, + { "fcmovu", ST, STi, XX }, + { "(bad)", XX, XX, XX }, + { FGRPda_5 }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + }, + /* db */ + { + { "fcmovnb",ST, STi, XX }, + { "fcmovne",ST, STi, XX }, + { "fcmovnbe",ST, STi, XX }, + { "fcmovnu",ST, STi, XX }, + { FGRPdb_4 }, + { "fucomi", ST, STi, XX }, + { "fcomi", ST, STi, XX }, + { "(bad)", XX, XX, XX }, + }, + /* dc */ + { + { "fadd", STi, ST, XX }, + { "fmul", STi, ST, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, +#if UNIXWARE_COMPAT + { "fsub", STi, ST, XX }, + { "fsubr", STi, ST, XX }, + { "fdiv", STi, ST, XX }, + { "fdivr", STi, ST, XX }, +#else + { "fsubr", STi, ST, XX }, + { "fsub", STi, ST, XX }, + { "fdivr", STi, ST, XX }, + { "fdiv", STi, ST, XX }, +#endif + }, + /* dd */ + { + { "ffree", STi, XX, XX }, + { "(bad)", XX, XX, XX }, + { "fst", STi, XX, XX }, + { "fstp", STi, XX, XX }, + { "fucom", STi, XX, XX }, + { "fucomp", STi, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + }, + /* de */ + { + { "faddp", STi, ST, XX }, + { "fmulp", STi, ST, XX }, + { "(bad)", XX, XX, XX }, + { FGRPde_3 }, +#if UNIXWARE_COMPAT + { "fsubp", STi, ST, XX }, + { "fsubrp", STi, ST, XX }, + { "fdivp", STi, ST, XX }, + { "fdivrp", STi, ST, XX }, +#else + { "fsubrp", STi, ST, XX }, + { "fsubp", STi, ST, XX }, + { "fdivrp", STi, ST, XX }, + { "fdivp", STi, ST, XX }, +#endif + }, + /* df */ + { + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { "(bad)", XX, XX, XX }, + { FGRPdf_4 }, + { "fucomip",ST, STi, XX }, + { "fcomip", ST, STi, XX }, + { "(bad)", XX, XX, XX }, + }, +}; + + +static char *fgrps[][8] = { + /* d9_2 0 */ + { + "fnop","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, + + /* d9_4 1 */ + { + "fchs","fabs","(bad)","(bad)","ftst","fxam","(bad)","(bad)", + }, + + /* d9_5 2 */ + { + "fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","(bad)", + }, + + /* d9_6 3 */ + { + "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp", + }, + + /* d9_7 4 */ + { + "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos", + }, + + /* da_5 5 */ + { + "(bad)","fucompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, + + /* db_4 6 */ + { + "feni(287 only)","fdisi(287 only)","fNclex","fNinit", + "fNsetpm(287 only)","(bad)","(bad)","(bad)", + }, + + /* de_3 7 */ + { + "(bad)","fcompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, + + /* df_4 8 */ + { + "fNstsw","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)", + }, +}; + +static void +dofloat (sizeflag) + int sizeflag; +{ + const struct dis386 *dp; + unsigned char floatop; + + floatop = codep[-1]; + + if (mod != 3) + { + if (intel_syntax) + putop (float_mem_intel[(floatop - 0xd8 ) * 8 + reg], sizeflag); + else + putop (float_mem_att[(floatop - 0xd8 ) * 8 + reg], sizeflag); + obufp = op1out; + if (floatop == 0xdb) + OP_E (x_mode, sizeflag); + else if (floatop == 0xdd) + OP_E (d_mode, sizeflag); + else + OP_E (v_mode, sizeflag); + return; + } + codep++; + + dp = &float_reg[floatop - 0xd8][reg]; + if (dp->name == NULL) + { + putop (fgrps[dp->bytemode1][rm], sizeflag); + + /* instruction fnstsw is only one with strange arg */ + if (floatop == 0xdf && codep[-1] == 0xe0) + strcpy (op1out, names16[0]); + } + else + { + putop (dp->name, sizeflag); + + obufp = op1out; + if (dp->op1) + (*dp->op1)(dp->bytemode1, sizeflag); + obufp = op2out; + if (dp->op2) + (*dp->op2)(dp->bytemode2, sizeflag); + } +} + +/* ARGSUSED */ +static void +OP_ST (ignore, sizeflag) + int ignore ATTRIBUTE_UNUSED; + int sizeflag ATTRIBUTE_UNUSED; +{ + oappend ("%st"); +} + +/* ARGSUSED */ +static void +OP_STi (ignore, sizeflag) + int ignore ATTRIBUTE_UNUSED; + int sizeflag ATTRIBUTE_UNUSED; +{ + sprintf (scratchbuf, "%%st(%d)", rm); + oappend (scratchbuf); +} + + +/* capital letters in template are macros */ +static void +putop (template, sizeflag) + const char *template; + int sizeflag; +{ + const char *p; + + for (p = template; *p; p++) + { + switch (*p) + { + default: + *obufp++ = *p; + break; + case 'A': + if (intel_syntax) + break; + if (mod != 3 +#ifdef SUFFIX_ALWAYS + || (sizeflag & SUFFIX_ALWAYS) +#endif + ) + *obufp++ = 'b'; + break; + case 'B': + if (intel_syntax) + break; +#ifdef SUFFIX_ALWAYS + if (sizeflag & SUFFIX_ALWAYS) + *obufp++ = 'b'; +#endif + break; + case 'E': /* For jcxz/jecxz */ + if (sizeflag & AFLAG) + *obufp++ = 'e'; + break; + case 'L': + if (intel_syntax) + break; +#ifdef SUFFIX_ALWAYS + if (sizeflag & SUFFIX_ALWAYS) + *obufp++ = 'l'; +#endif + break; + case 'N': + if ((prefixes & PREFIX_FWAIT) == 0) + *obufp++ = 'n'; + else + used_prefixes |= PREFIX_FWAIT; + break; + case 'P': + if (intel_syntax) + break; + if ((prefixes & PREFIX_DATA) +#ifdef SUFFIX_ALWAYS + || (sizeflag & SUFFIX_ALWAYS) +#endif + ) + { + if (sizeflag & DFLAG) + *obufp++ = 'l'; + else + *obufp++ = 'w'; + used_prefixes |= (prefixes & PREFIX_DATA); + } + break; + case 'Q': + if (intel_syntax) + break; + if (mod != 3 +#ifdef SUFFIX_ALWAYS + || (sizeflag & SUFFIX_ALWAYS) +#endif + ) + { + if (sizeflag & DFLAG) + *obufp++ = 'l'; + else + *obufp++ = 'w'; + used_prefixes |= (prefixes & PREFIX_DATA); + } + break; + case 'R': + if (intel_syntax) + { + if (sizeflag & DFLAG) + { + *obufp++ = 'd'; + *obufp++ = 'q'; + } + else + { + *obufp++ = 'w'; + *obufp++ = 'd'; + } + } + else + { + if (sizeflag & DFLAG) + *obufp++ = 'l'; + else + *obufp++ = 'w'; + } + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case 'S': + if (intel_syntax) + break; +#ifdef SUFFIX_ALWAYS + if (sizeflag & SUFFIX_ALWAYS) + { + if (sizeflag & DFLAG) + *obufp++ = 'l'; + else + *obufp++ = 'w'; + used_prefixes |= (prefixes & PREFIX_DATA); + } +#endif + break; + case 'W': + /* operand size flag for cwtl, cbtw */ + if (sizeflag & DFLAG) + *obufp++ = 'w'; + else + *obufp++ = 'b'; + if (intel_syntax) + { + if (sizeflag & DFLAG) + { + *obufp++ = 'd'; + *obufp++ = 'e'; + } + else + { + *obufp++ = 'w'; + } + } + used_prefixes |= (prefixes & PREFIX_DATA); + break; + } + } + *obufp = 0; +} + +static void +oappend (s) + const char *s; +{ + strcpy (obufp, s); + obufp += strlen (s); +} + +static void +append_seg () +{ + if (prefixes & PREFIX_CS) + { + oappend ("%cs:"); + used_prefixes |= PREFIX_CS; + } + if (prefixes & PREFIX_DS) + { + oappend ("%ds:"); + used_prefixes |= PREFIX_DS; + } + if (prefixes & PREFIX_SS) + { + oappend ("%ss:"); + used_prefixes |= PREFIX_SS; + } + if (prefixes & PREFIX_ES) + { + oappend ("%es:"); + used_prefixes |= PREFIX_ES; + } + if (prefixes & PREFIX_FS) + { + oappend ("%fs:"); + used_prefixes |= PREFIX_FS; + } + if (prefixes & PREFIX_GS) + { + oappend ("%gs:"); + used_prefixes |= PREFIX_GS; + } +} + +static void +OP_indirE (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + if (!intel_syntax) + oappend ("*"); + OP_E (bytemode, sizeflag); +} + +static void +OP_E (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + int disp; + + /* skip mod/rm byte */ + codep++; + + if (mod == 3) + { + switch (bytemode) + { + case b_mode: + oappend (names8[rm]); + break; + case w_mode: + oappend (names16[rm]); + break; + case d_mode: + oappend (names32[rm]); + break; + case v_mode: + if (sizeflag & DFLAG) + oappend (names32[rm]); + else + oappend (names16[rm]); + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case 0: + if ( !(codep[-2] == 0xAE && codep[-1] == 0xF8 /* sfence */)) + BadOp(); /* bad sfence,lea,lds,les,lfs,lgs,lss modrm */ + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + break; + } + return; + } + + disp = 0; + append_seg (); + + if (sizeflag & AFLAG) /* 32 bit address mode */ + { + int havesib; + int havebase; + int base; + int index = 0; + int scale = 0; + + havesib = 0; + havebase = 1; + base = rm; + + if (base == 4) + { + havesib = 1; + FETCH_DATA (the_info, codep + 1); + scale = (*codep >> 6) & 3; + index = (*codep >> 3) & 7; + base = *codep & 7; + codep++; + } + + switch (mod) + { + case 0: + if (base == 5) + { + havebase = 0; + disp = get32 (); + } + break; + case 1: + FETCH_DATA (the_info, codep + 1); + disp = *codep++; + if ((disp & 0x80) != 0) + disp -= 0x100; + break; + case 2: + disp = get32 (); + break; + } + + if (!intel_syntax) + if (mod != 0 || base == 5) + { + sprintf (scratchbuf, "0x%x", disp); + oappend (scratchbuf); + } + + if (havebase || (havesib && (index != 4 || scale != 0))) + { + if (intel_syntax) + { + switch (bytemode) + { + case b_mode: + oappend("BYTE PTR "); + break; + case w_mode: + oappend("WORD PTR "); + break; + case v_mode: + oappend("DWORD PTR "); + break; + case d_mode: + oappend("QWORD PTR "); + break; + case x_mode: + oappend("XWORD PTR "); + break; + default: + break; + } + } + *obufp++ = open_char; + *obufp = '\0'; + if (havebase) + oappend (names32[base]); + if (havesib) + { + if (index != 4) + { + if (intel_syntax) + { + if (havebase) + { + *obufp++ = separator_char; + *obufp = '\0'; + } + sprintf (scratchbuf, "%s", names32[index]); + } + else + sprintf (scratchbuf, ",%s", names32[index]); + oappend (scratchbuf); + } + if (!intel_syntax + || (intel_syntax + && bytemode != b_mode + && bytemode != w_mode + && bytemode != v_mode)) + { + *obufp++ = scale_char; + *obufp = '\0'; + sprintf (scratchbuf, "%d", 1 << scale); + oappend (scratchbuf); + } + } + if (intel_syntax) + if (mod != 0 || base == 5) + { + /* Don't print zero displacements */ + if (disp > 0) + { + sprintf (scratchbuf, "+%d", disp); + oappend (scratchbuf); + } + else if (disp < 0) + { + sprintf (scratchbuf, "%d", disp); + oappend (scratchbuf); + } + } + + *obufp++ = close_char; + *obufp = '\0'; + } + else if (intel_syntax) + { + if (mod != 0 || base == 5) + { + if (prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS + | PREFIX_ES | PREFIX_FS | PREFIX_GS)) + ; + else + { + oappend (names_seg[3]); + oappend (":"); + } + sprintf (scratchbuf, "0x%x", disp); + oappend (scratchbuf); + } + } + } + else + { /* 16 bit address mode */ + switch (mod) + { + case 0: + if (rm == 6) + { + disp = get16 (); + if ((disp & 0x8000) != 0) + disp -= 0x10000; + } + break; + case 1: + FETCH_DATA (the_info, codep + 1); + disp = *codep++; + if ((disp & 0x80) != 0) + disp -= 0x100; + break; + case 2: + disp = get16 (); + if ((disp & 0x8000) != 0) + disp -= 0x10000; + break; + } + + if (!intel_syntax) + if (mod != 0 || rm == 6) + { + sprintf (scratchbuf, "%d", disp); + oappend (scratchbuf); + } + + if (mod != 0 || rm != 6) + { + *obufp++ = open_char; + *obufp = '\0'; + oappend (index16[rm]); + *obufp++ = close_char; + *obufp = '\0'; + } + } +} + +static void +OP_G (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + switch (bytemode) + { + case b_mode: + oappend (names8[reg]); + break; + case w_mode: + oappend (names16[reg]); + break; + case d_mode: + oappend (names32[reg]); + break; + case v_mode: + if (sizeflag & DFLAG) + oappend (names32[reg]); + else + oappend (names16[reg]); + used_prefixes |= (prefixes & PREFIX_DATA); + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + break; + } +} + +static int +get32 () +{ + int x = 0; + + FETCH_DATA (the_info, codep + 4); + x = *codep++ & 0xff; + x |= (*codep++ & 0xff) << 8; + x |= (*codep++ & 0xff) << 16; + x |= (*codep++ & 0xff) << 24; + return x; +} + +static int +get16 () +{ + int x = 0; + + FETCH_DATA (the_info, codep + 2); + x = *codep++ & 0xff; + x |= (*codep++ & 0xff) << 8; + return x; +} + +static void +set_op (op) + unsigned int op; +{ + op_index[op_ad] = op_ad; + op_address[op_ad] = op; +} + +static void +OP_REG (code, sizeflag) + int code; + int sizeflag; +{ + const char *s; + + switch (code) + { + case indir_dx_reg: + s = "(%dx)"; + break; + case ax_reg: case cx_reg: case dx_reg: case bx_reg: + case sp_reg: case bp_reg: case si_reg: case di_reg: + s = names16[code - ax_reg]; + break; + case es_reg: case ss_reg: case cs_reg: + case ds_reg: case fs_reg: case gs_reg: + s = names_seg[code - es_reg]; + break; + case al_reg: case ah_reg: case cl_reg: case ch_reg: + case dl_reg: case dh_reg: case bl_reg: case bh_reg: + s = names8[code - al_reg]; + break; + case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg: + case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg: + if (sizeflag & DFLAG) + s = names32[code - eAX_reg]; + else + s = names16[code - eAX_reg]; + used_prefixes |= (prefixes & PREFIX_DATA); + break; + default: + s = INTERNAL_DISASSEMBLER_ERROR; + break; + } + oappend (s); +} + +static void +OP_I (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + int op; + + switch (bytemode) + { + case b_mode: + FETCH_DATA (the_info, codep + 1); + op = *codep++ & 0xff; + break; + case v_mode: + if (sizeflag & DFLAG) + op = get32 (); + else + op = get16 (); + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case w_mode: + op = get16 (); + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + return; + } + + if (intel_syntax) + sprintf (scratchbuf, "0x%x", op); + else + sprintf (scratchbuf, "$0x%x", op); + oappend (scratchbuf); + scratchbuf[0] = '\0'; +} + +static void +OP_sI (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + int op; + + switch (bytemode) + { + case b_mode: + FETCH_DATA (the_info, codep + 1); + op = *codep++; + if ((op & 0x80) != 0) + op -= 0x100; + break; + case v_mode: + if (sizeflag & DFLAG) + op = get32 (); + else + { + op = get16(); + if ((op & 0x8000) != 0) + op -= 0x10000; + } + used_prefixes |= (prefixes & PREFIX_DATA); + break; + case w_mode: + op = get16 (); + if ((op & 0x8000) != 0) + op -= 0x10000; + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + return; + } + if (intel_syntax) + sprintf (scratchbuf, "%d", op); + else + sprintf (scratchbuf, "$0x%x", op); + oappend (scratchbuf); +} + +static void +OP_J (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + int disp; + int mask = -1; + + switch (bytemode) + { + case b_mode: + FETCH_DATA (the_info, codep + 1); + disp = *codep++; + if ((disp & 0x80) != 0) + disp -= 0x100; + break; + case v_mode: + if (sizeflag & DFLAG) + disp = get32 (); + else + { + disp = get16 (); + /* for some reason, a data16 prefix on a jump instruction + means that the pc is masked to 16 bits after the + displacement is added! */ + mask = 0xffff; + } + used_prefixes |= (prefixes & PREFIX_DATA); + break; + default: + oappend (INTERNAL_DISASSEMBLER_ERROR); + return; + } + disp = (start_pc + codep - start_codep + disp) & mask; + set_op (disp); + sprintf (scratchbuf, "0x%x", disp); + oappend (scratchbuf); +} + +/* ARGSUSED */ +static void +OP_SEG (dummy, sizeflag) + int dummy ATTRIBUTE_UNUSED; + int sizeflag ATTRIBUTE_UNUSED; +{ + static char *sreg[] = { + "%es","%cs","%ss","%ds","%fs","%gs","%?","%?", + }; + + oappend (sreg[reg]); +} + +/* ARGSUSED */ +static void +OP_DIR (dummy, sizeflag) + int dummy ATTRIBUTE_UNUSED; + int sizeflag; +{ + int seg, offset; + + if (sizeflag & DFLAG) + { + offset = get32 (); + seg = get16 (); + } + else + { + offset = get16 (); + seg = get16 (); + } + used_prefixes |= (prefixes & PREFIX_DATA); + sprintf (scratchbuf, "$0x%x,$0x%x", seg, offset); + oappend (scratchbuf); +} + +/* ARGSUSED */ +static void +OP_OFF (ignore, sizeflag) + int ignore ATTRIBUTE_UNUSED; + int sizeflag; +{ + int off; + + append_seg (); + + if (sizeflag & AFLAG) + off = get32 (); + else + off = get16 (); + + if (intel_syntax) + { + if (!(prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS + | PREFIX_ES | PREFIX_FS | PREFIX_GS))) + { + oappend (names_seg[3]); + oappend (":"); + } + } + sprintf (scratchbuf, "0x%x", off); + oappend (scratchbuf); +} + +static void +ptr_reg (code, sizeflag) + int code; + int sizeflag; +{ + const char *s; + oappend ("("); + if (sizeflag & AFLAG) + s = names32[code - eAX_reg]; + else + s = names16[code - eAX_reg]; + oappend (s); + oappend (")"); +} + +static void +OP_ESreg (code, sizeflag) + int code; + int sizeflag; +{ + oappend ("%es:"); + ptr_reg (code, sizeflag); +} + +static void +OP_DSreg (code, sizeflag) + int code; + int sizeflag; +{ + if ((prefixes + & (PREFIX_CS + | PREFIX_DS + | PREFIX_SS + | PREFIX_ES + | PREFIX_FS + | PREFIX_GS)) == 0) + prefixes |= PREFIX_DS; + append_seg(); + ptr_reg (code, sizeflag); +} + +/* ARGSUSED */ +static void +OP_C (dummy, sizeflag) + int dummy ATTRIBUTE_UNUSED; + int sizeflag ATTRIBUTE_UNUSED; +{ + sprintf (scratchbuf, "%%cr%d", reg); + oappend (scratchbuf); +} + +/* ARGSUSED */ +static void +OP_D (dummy, sizeflag) + int dummy ATTRIBUTE_UNUSED; + int sizeflag ATTRIBUTE_UNUSED; +{ + sprintf (scratchbuf, "%%db%d", reg); + oappend (scratchbuf); +} + +/* ARGSUSED */ +static void +OP_T (dummy, sizeflag) + int dummy ATTRIBUTE_UNUSED; + int sizeflag ATTRIBUTE_UNUSED; +{ + sprintf (scratchbuf, "%%tr%d", reg); + oappend (scratchbuf); +} + +static void +OP_Rd (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + if (mod == 3) + OP_E (bytemode, sizeflag); + else + BadOp(); +} + +static void +OP_MMX (ignore, sizeflag) + int ignore ATTRIBUTE_UNUSED; + int sizeflag ATTRIBUTE_UNUSED; +{ + sprintf (scratchbuf, "%%mm%d", reg); + oappend (scratchbuf); +} + +static void +OP_XMM (bytemode, sizeflag) + int bytemode ATTRIBUTE_UNUSED; + int sizeflag ATTRIBUTE_UNUSED; +{ + sprintf (scratchbuf, "%%xmm%d", reg); + oappend (scratchbuf); +} + +static void +OP_EM (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + if (mod != 3) + { + OP_E (bytemode, sizeflag); + return; + } + + codep++; + sprintf (scratchbuf, "%%mm%d", rm); + oappend (scratchbuf); +} + +static void +OP_EX (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + if (mod != 3) + { + OP_E (bytemode, sizeflag); + return; + } + + codep++; + sprintf (scratchbuf, "%%xmm%d", rm); + oappend (scratchbuf); +} + +static void +OP_MS (bytemode, sizeflag) + int bytemode; + int sizeflag; +{ + if (mod == 3) + OP_EM (bytemode, sizeflag); + else + BadOp(); +} + +static const char *Suffix3DNow[] = { +/* 00 */ NULL, NULL, NULL, NULL, +/* 04 */ NULL, NULL, NULL, NULL, +/* 08 */ NULL, NULL, NULL, NULL, +/* 0C */ "pi2fw", "pi2fd", NULL, NULL, +/* 10 */ NULL, NULL, NULL, NULL, +/* 14 */ NULL, NULL, NULL, NULL, +/* 18 */ NULL, NULL, NULL, NULL, +/* 1C */ "pf2iw", "pf2id", NULL, NULL, +/* 20 */ NULL, NULL, NULL, NULL, +/* 24 */ NULL, NULL, NULL, NULL, +/* 28 */ NULL, NULL, NULL, NULL, +/* 2C */ NULL, NULL, NULL, NULL, +/* 30 */ NULL, NULL, NULL, NULL, +/* 34 */ NULL, NULL, NULL, NULL, +/* 38 */ NULL, NULL, NULL, NULL, +/* 3C */ NULL, NULL, NULL, NULL, +/* 40 */ NULL, NULL, NULL, NULL, +/* 44 */ NULL, NULL, NULL, NULL, +/* 48 */ NULL, NULL, NULL, NULL, +/* 4C */ NULL, NULL, NULL, NULL, +/* 50 */ NULL, NULL, NULL, NULL, +/* 54 */ NULL, NULL, NULL, NULL, +/* 58 */ NULL, NULL, NULL, NULL, +/* 5C */ NULL, NULL, NULL, NULL, +/* 60 */ NULL, NULL, NULL, NULL, +/* 64 */ NULL, NULL, NULL, NULL, +/* 68 */ NULL, NULL, NULL, NULL, +/* 6C */ NULL, NULL, NULL, NULL, +/* 70 */ NULL, NULL, NULL, NULL, +/* 74 */ NULL, NULL, NULL, NULL, +/* 78 */ NULL, NULL, NULL, NULL, +/* 7C */ NULL, NULL, NULL, NULL, +/* 80 */ NULL, NULL, NULL, NULL, +/* 84 */ NULL, NULL, NULL, NULL, +/* 88 */ NULL, NULL, "pfnacc", NULL, +/* 8C */ NULL, NULL, "pfpnacc", NULL, +/* 90 */ "pfcmpge", NULL, NULL, NULL, +/* 94 */ "pfmin", NULL, "pfrcp", "pfrsqrt", +/* 98 */ NULL, NULL, "pfsub", NULL, +/* 9C */ NULL, NULL, "pfadd", NULL, +/* A0 */ "pfcmpgt", NULL, NULL, NULL, +/* A4 */ "pfmax", NULL, "pfrcpit1", "pfrsqit1", +/* A8 */ NULL, NULL, "pfsubr", NULL, +/* AC */ NULL, NULL, "pfacc", NULL, +/* B0 */ "pfcmpeq", NULL, NULL, NULL, +/* B4 */ "pfmul", NULL, "pfrcpit2", "pfmulhrw", +/* B8 */ NULL, NULL, NULL, "pswapd", +/* BC */ NULL, NULL, NULL, "pavgusb", +/* C0 */ NULL, NULL, NULL, NULL, +/* C4 */ NULL, NULL, NULL, NULL, +/* C8 */ NULL, NULL, NULL, NULL, +/* CC */ NULL, NULL, NULL, NULL, +/* D0 */ NULL, NULL, NULL, NULL, +/* D4 */ NULL, NULL, NULL, NULL, +/* D8 */ NULL, NULL, NULL, NULL, +/* DC */ NULL, NULL, NULL, NULL, +/* E0 */ NULL, NULL, NULL, NULL, +/* E4 */ NULL, NULL, NULL, NULL, +/* E8 */ NULL, NULL, NULL, NULL, +/* EC */ NULL, NULL, NULL, NULL, +/* F0 */ NULL, NULL, NULL, NULL, +/* F4 */ NULL, NULL, NULL, NULL, +/* F8 */ NULL, NULL, NULL, NULL, +/* FC */ NULL, NULL, NULL, NULL, +}; + +static void +OP_3DNowSuffix (bytemode, sizeflag) + int bytemode ATTRIBUTE_UNUSED; + int sizeflag ATTRIBUTE_UNUSED; +{ + const char *mnemonic; + + FETCH_DATA (the_info, codep + 1); + /* AMD 3DNow! instructions are specified by an opcode suffix in the + place where an 8-bit immediate would normally go. ie. the last + byte of the instruction. */ + obufp = obuf + strlen(obuf); + mnemonic = Suffix3DNow[*codep++ & 0xff]; + if (mnemonic) + oappend (mnemonic); + else + { + /* Since a variable sized modrm/sib chunk is between the start + of the opcode (0x0f0f) and the opcode suffix, we need to do + all the modrm processing first, and don't know until now that + we have a bad opcode. This necessitates some cleaning up. */ + op1out[0] = '\0'; + op2out[0] = '\0'; + BadOp(); + } +} + + +static const char *simd_cmp_op [] = { + "eq", + "lt", + "le", + "unord", + "neq", + "nlt", + "nle", + "ord" +}; + +static void +OP_SIMD_Suffix (bytemode, sizeflag) + int bytemode ATTRIBUTE_UNUSED; + int sizeflag ATTRIBUTE_UNUSED; +{ + unsigned int cmp_type; + + FETCH_DATA (the_info, codep + 1); + obufp = obuf + strlen(obuf); + cmp_type = *codep++ & 0xff; + if (cmp_type < 8) + { + sprintf (scratchbuf, "cmp%s%cs", + simd_cmp_op[cmp_type], + prefixes & PREFIX_REPZ ? 's' : 'p'); + used_prefixes |= (prefixes & PREFIX_REPZ); + oappend (scratchbuf); + } + else + { + /* We have a bad extension byte. Clean up. */ + op1out[0] = '\0'; + op2out[0] = '\0'; + BadOp(); + } +} + +static void +SIMD_Fixup (extrachar, sizeflag) + int extrachar; + int sizeflag ATTRIBUTE_UNUSED; +{ + /* Change movlps/movhps to movhlps/movlhps for 2 register operand + forms of these instructions. */ + if (mod == 3) + { + char *p = obuf + strlen(obuf); + *(p+1) = '\0'; + *p = *(p-1); + *(p-1) = *(p-2); + *(p-2) = *(p-3); + *(p-3) = extrachar; + } +} + +static void BadOp (void) +{ + codep = insn_codep + 1; /* throw away prefixes and 1st. opcode byte */ + oappend ("(bad)"); +} diff -urN 2.4.0/arch/i386/kdb/kdba_bp.c 2.4.0-ikd-1/arch/i386/kdb/kdba_bp.c --- 2.4.0/arch/i386/kdb/kdba_bp.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/arch/i386/kdb/kdba_bp.c Mon Jan 15 18:40:04 2001 @@ -0,0 +1,770 @@ +/* + * Kernel Debugger Architecture Dependent Breakpoint Handling + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ + +#include +#include +#include +#include +#include +#include +#include + + +static char *kdba_rwtypes[] = { "Instruction(Register)", "Data Write", + "I/O", "Data Access"}; + +/* + * Table describing processor architecture hardware + * breakpoint registers. + */ + +kdbhard_bp_t kdb_hardbreaks[KDB_MAXHARDBPT]; + +/* + * kdba_db_trap + * + * Perform breakpoint processing upon entry to the + * processor debugger fault. Determine and print + * the active breakpoint. + * + * Parameters: + * ef Exception frame containing machine register state + * Outputs: + * None. + * Returns: + * KDB_DB_BPT Standard instruction or data breakpoint encountered + * KDB_DB_SS Single Step fault ('ss' command or end of 'ssb' command) + * KDB_DB_SSB Single Step fault, caller should continue ('ssb' command) + * KDB_DB_SSBPT Single step over breakpoint + * KDB_DB_NOBPT No existing kdb breakpoint matches this debug exception + * Locking: + * None. + * Remarks: + * Yup, there be goto's here. + * + * If multiple processors receive debug exceptions simultaneously, + * one may be waiting at the kdb fence in kdb() while the user + * issues a 'bc' command to clear the breakpoint the processor + * which is waiting has already encountered. If this is the case, + * the debug registers will no longer match any entry in the + * breakpoint table, and we'll return the value KDB_DB_NOBPT. + * This can cause a panic in die_if_kernel(). It is safer to + * disable the breakpoint (bd), go until all processors are past + * the breakpoint then clear the breakpoint (bc). This code + * recognises a breakpoint even when disabled but not when it has + * been cleared. + * + * WARNING: This routine clears the debug state. It should be called + * once per debug and the result cached. + */ + +kdb_dbtrap_t +kdba_db_trap(kdb_eframe_t ef) +{ + kdb_machreg_t dr6; + kdb_machreg_t dr7; + int rw, reg; + int i; + kdb_dbtrap_t rv = KDB_DB_BPT; + kdb_bp_t *bp; + + dr6 = kdb_getdr6(); + dr7 = kdb_getdr7(); + + if (KDB_DEBUG(BP)) + kdb_printf("kdb: dr6 0x%lx dr7 0x%lx\n", dr6, dr7); + if (dr6 & DR6_BS) { + if (KDB_STATE(SSBPT)) { + if (KDB_DEBUG(BP)) + kdb_printf("ssbpt\n"); + KDB_STATE_CLEAR(SSBPT); + for(i=0,bp=kdb_breakpoints; + i < KDB_MAXBPT; + i++, bp++) { + if (KDB_DEBUG(BP)) + kdb_printf("bp 0x%p enabled %d delayed %d global %d cpu %d\n", + bp, bp->bp_enabled, bp->bp_delayed, bp->bp_global, bp->bp_cpu); + if (!bp->bp_enabled) + continue; + if (!bp->bp_global && bp->bp_cpu != smp_processor_id()) + continue; + if (KDB_DEBUG(BP)) + kdb_printf("bp for this cpu\n"); + if (bp->bp_delayed) { + bp->bp_delayed = 0; + if (KDB_DEBUG(BP)) + kdb_printf("kdba_installbp\n"); + kdba_installbp(ef, bp); + if (!KDB_STATE(DOING_SS)) { + ef->eflags &= ~EF_TF; + return(KDB_DB_SSBPT); + } + break; + } + } + if (i == KDB_MAXBPT) { + kdb_printf("kdb: Unable to find delayed breakpoint\n"); + } + if (!KDB_STATE(DOING_SS)) { + ef->eflags &= ~EF_TF; + return(KDB_DB_NOBPT); + } + /* FALLTHROUGH */ + } + + /* + * KDB_STATE_DOING_SS is set when the kernel debugger is using + * the processor trap flag to single-step a processor. If a + * single step trap occurs and this flag is clear, the SS trap + * will be ignored by KDB and the kernel will be allowed to deal + * with it as necessary (e.g. for ptrace). + */ + if (!KDB_STATE(DOING_SS)) + goto unknown; + + /* single step */ + rv = KDB_DB_SS; /* Indicate single step */ + if (KDB_STATE(DOING_SSB)) { + unsigned char op1, op2 = 0; + + kdb_id1(ef->eip); + op1 = (unsigned char)kdba_getword(ef->eip, sizeof(op1)); + if (op1 == 0x0f) { + op2 = (unsigned char)kdba_getword(ef->eip+1, sizeof(op2)); + } + if (((op1&0xf0) == 0xe0) /* short disp jumps */ + || ((op1&0xf0) == 0x70) /* Misc. jumps */ + || (op1 == 0xc2) /* ret */ + || (op1 == 0x9a) /* call */ + || ((op1&0xf8) == 0xc8) /* enter, leave, iret, int, */ + || ((op1 == 0x0f) + && ((op2&0xf0)== 0x80))) { + /* + * End the ssb command here. + */ + KDB_STATE_CLEAR(DOING_SSB); + KDB_STATE_CLEAR(DOING_SS); + } else { + rv = KDB_DB_SSB; /* Indicate ssb - dismiss immediately */ + } + } else { + /* + * Print current insn + */ + kdb_printf("SS trap at "); + kdb_symbol_print(ef->eip, NULL, KDB_SP_DEFAULT|KDB_SP_NEWLINE); + kdb_id1(ef->eip); + KDB_STATE_CLEAR(DOING_SS); + } + + if (rv != KDB_DB_SSB) + ef->eflags &= ~EF_TF; + } + + if (dr6 & DR6_B0) { + rw = DR7_RW0(dr7); + reg = 0; + goto handle; + } + + if (dr6 & DR6_B1) { + rw = DR7_RW1(dr7); + reg = 1; + goto handle; + } + + if (dr6 & DR6_B2) { + rw = DR7_RW2(dr7); + reg = 2; + goto handle; + } + + if (dr6 & DR6_B3) { + rw = DR7_RW3(dr7); + reg = 3; + goto handle; + } + + if (rv > 0) + goto handled; + + goto unknown; /* dismiss */ + +handle: + /* + * Set Resume Flag + */ + ef->eflags |= EF_RF; + + /* + * Determine which breakpoint was encountered. + */ + for(i=0, bp=kdb_breakpoints; ibp_free) + && (bp->bp_global || bp->bp_cpu == smp_processor_id()) + && (bp->bp_hard) + && (bp->bp_hard->bph_reg == reg)) { + /* + * Hit this breakpoint. + */ + kdb_printf("%s breakpoint #%d at " kdb_bfd_vma_fmt "\n", + kdba_rwtypes[rw], + i, bp->bp_addr); + + /* + * For an instruction breakpoint, disassemble + * the current instruction. + */ + if (rw == 0) { + kdb_id1(ef->eip); + } + + goto handled; + } + } + +unknown: + ef->eflags |= EF_RF; /* Supress further faults */ + rv = KDB_DB_NOBPT; /* Cause kdb() to return */ + +handled: + + /* + * Clear the pending exceptions. + */ + kdb_putdr6(0); + + return rv; +} + +/* + * kdba_bp_trap + * + * Perform breakpoint processing upon entry to the + * processor breakpoint instruction fault. Determine and print + * the active breakpoint. + * + * Parameters: + * ef Exception frame containing machine register state + * Outputs: + * None. + * Returns: + * 0 Standard instruction or data breakpoint encountered + * 1 Single Step fault ('ss' command) + * 2 Single Step fault, caller should continue ('ssb' command) + * 3 No existing kdb breakpoint matches this debug exception + * Locking: + * None. + * Remarks: + * + * If multiple processors receive debug exceptions simultaneously, + * one may be waiting at the kdb fence in kdb() while the user + * issues a 'bc' command to clear the breakpoint the processor which + * is waiting has already encountered. If this is the case, the + * debug registers will no longer match any entry in the breakpoint + * table, and we'll return the value '3'. This can cause a panic + * in die_if_kernel(). It is safer to disable the breakpoint (bd), + * 'go' until all processors are past the breakpoint then clear the + * breakpoint (bc). This code recognises a breakpoint even when + * disabled but not when it has been cleared. + * + * WARNING: This routine resets the eip. It should be called + * once per breakpoint and the result cached. + */ + +kdb_dbtrap_t +kdba_bp_trap(kdb_eframe_t ef) +{ + int i; + kdb_dbtrap_t rv; + kdb_bp_t *bp; + + /* + * Determine which breakpoint was encountered. + */ + if (KDB_DEBUG(BP)) + kdb_printf("kdba_bp_trap: eip=0x%lx (not adjusted) " + "eflags=0x%lx ef=0x%p esp=0x%lx\n", + ef->eip, ef->eflags, ef, ef->esp); + + rv = KDB_DB_NOBPT; /* Cause kdb() to return */ + + for(i=0, bp=kdb_breakpoints; ibp_free) + continue; + if (!bp->bp_global && bp->bp_cpu != smp_processor_id()) + continue; + if (bp->bp_addr == (ef->eip - bp->bp_adjust)) { + /* Hit this breakpoint. */ + ef->eip -= bp->bp_adjust; + kdb_printf("Instruction(i) breakpoint #%d at 0x%lx (adjusted)\n", + i, ef->eip); + kdb_id1(ef->eip); + rv = KDB_DB_BPT; + bp->bp_delay = 1; + break; + } + } + + return rv; +} + +/* + * kdba_handle_bp + * + * Handle an instruction-breakpoint trap. Called when re-installing + * an enabled breakpoint which has has the bp_delay bit set. + * + * Parameters: + * Returns: + * Locking: + * Remarks: + * + * Ok, we really need to: + * 1) Restore the original instruction byte + * 2) Single Step + * 3) Restore breakpoint instruction + * 4) Continue. + * + * + */ + +static void +kdba_handle_bp(kdb_eframe_t ef, kdb_bp_t *bp) +{ + if (KDB_DEBUG(BP)) + kdb_printf("ef->eip = 0x%lx\n", ef->eip); + + /* + * Setup single step + */ + kdba_setsinglestep(ef); + + /* KDB_STATE_SSBPT is set when the kernel debugger must single step + * a task in order to re-establish an instruction breakpoint which + * uses the instruction replacement mechanism. + */ + KDB_STATE_SET(SSBPT); + + /* + * Reset delay attribute + */ + bp->bp_delay = 0; + bp->bp_delayed = 1; +} + + +/* + * kdba_bptype + * + * Return a string describing type of breakpoint. + * + * Parameters: + * bph Pointer to hardware breakpoint description + * Outputs: + * None. + * Returns: + * Character string. + * Locking: + * None. + * Remarks: + */ + +char * +kdba_bptype(kdbhard_bp_t *bph) +{ + char *mode; + + mode = kdba_rwtypes[bph->bph_mode]; + + return mode; +} + +/* + * kdba_printbpreg + * + * Print register name assigned to breakpoint + * + * Parameters: + * bph Pointer hardware breakpoint structure + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdba_printbpreg(kdbhard_bp_t *bph) +{ + kdb_printf(" in dr%ld", bph->bph_reg); +} + +/* + * kdba_printbp + * + * Print string describing hardware breakpoint. + * + * Parameters: + * bph Pointer to hardware breakpoint description + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdba_printbp(kdb_bp_t *bp) +{ + kdb_printf("\n is enabled"); + if (bp->bp_hardtype) { + kdba_printbpreg(bp->bp_hard); + if (bp->bp_hard->bph_mode != 0) { + kdb_printf(" for %d bytes", + bp->bp_hard->bph_length+1); + } + } +} + +/* + * kdba_parsebp + * + * Parse architecture dependent portion of the + * breakpoint command. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + * for Ia32 architure, data access, data write and + * I/O breakpoints are supported in addition to instruction + * breakpoints. + * + * {datar|dataw|io|inst} [length] + */ + +int +kdba_parsebp(int argc, const char **argv, int *nextargp, kdb_bp_t *bp) +{ + int nextarg = *nextargp; + int diag; + kdbhard_bp_t *bph = &bp->bp_template; + + bph->bph_mode = 0; /* Default to instruction breakpoint */ + bph->bph_length = 0; /* Length must be zero for insn bp */ + if ((argc + 1) != nextarg) { + if (strnicmp(argv[nextarg], "datar", sizeof("datar")) == 0) { + bph->bph_mode = 3; + } else if (strnicmp(argv[nextarg], "dataw", sizeof("dataw")) == 0) { + bph->bph_mode = 1; + } else if (strnicmp(argv[nextarg], "io", sizeof("io")) == 0) { + bph->bph_mode = 2; + } else if (strnicmp(argv[nextarg], "inst", sizeof("inst")) == 0) { + bph->bph_mode = 0; + } else { + return KDB_ARGCOUNT; + } + + bph->bph_length = 3; /* Default to 4 byte */ + + nextarg++; + + if ((argc + 1) != nextarg) { + unsigned long len; + + diag = kdbgetularg((char *)argv[nextarg], + &len); + if (diag) + return diag; + + + if ((len > 4) || (len == 3)) + return KDB_BADLENGTH; + + bph->bph_length = len; + bph->bph_length--; /* Normalize for debug register */ + nextarg++; + } + + if ((argc + 1) != nextarg) + return KDB_ARGCOUNT; + + /* + * Indicate to architecture independent level that + * a hardware register assignment is required to enable + * this breakpoint. + */ + + bph->bph_free = 0; + } else { + if (KDB_DEBUG(BP)) + kdb_printf("kdba_bp: no args, forcehw is %d\n", bp->bp_forcehw); + if (bp->bp_forcehw) { + /* + * We are forced to use a hardware register for this + * breakpoint because either the bph or bpha + * commands were used to establish this breakpoint. + */ + bph->bph_free = 0; + } else { + /* + * Indicate to architecture dependent level that + * the instruction replacement breakpoint technique + * should be used for this breakpoint. + */ + bph->bph_free = 1; + bp->bp_adjust = 1; /* software, int 3 is one byte */ + } + } + + *nextargp = nextarg; + return 0; +} + +/* + * kdba_allocbp + * + * Associate a hardware register with a breakpoint. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * A pointer to the allocated register kdbhard_bp_t structure for + * success, Null and a non-zero diagnostic for failure. + * Locking: + * None. + * Remarks: + */ + +kdbhard_bp_t * +kdba_allocbp(kdbhard_bp_t *bph, int *diagp) +{ + int i; + kdbhard_bp_t *newbph; + + for(i=0,newbph=kdb_hardbreaks; i < KDB_MAXHARDBPT; i++, newbph++) { + if (newbph->bph_free) { + break; + } + } + + if (i == KDB_MAXHARDBPT) { + *diagp = KDB_TOOMANYDBREGS; + return NULL; + } + + *diagp = 0; + + /* + * Copy data from template. Can't just copy the entire template + * here because the register number in kdb_hardbreaks must be + * preserved. + */ + newbph->bph_data = bph->bph_data; + newbph->bph_write = bph->bph_write; + newbph->bph_mode = bph->bph_mode; + newbph->bph_length = bph->bph_length; + + /* + * Mark entry allocated. + */ + newbph->bph_free = 0; + + return newbph; +} + +/* + * kdba_freebp + * + * Deallocate a hardware breakpoint + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + */ + +void +kdba_freebp(kdbhard_bp_t *bph) +{ + bph->bph_free = 1; +} + +/* + * kdba_initbp + * + * Initialize the breakpoint table for the hardware breakpoint + * register. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + * + * There is one entry per register. On the ia32 architecture + * all the registers are interchangeable, so no special allocation + * criteria are required. + */ + +void +kdba_initbp(void) +{ + int i; + kdbhard_bp_t *bph; + + /* + * Clear the hardware breakpoint table + */ + + memset(kdb_hardbreaks, '\0', sizeof(kdb_hardbreaks)); + + for(i=0,bph=kdb_hardbreaks; ibph_reg = i; + bph->bph_free = 1; + } +} + +/* + * kdba_installbp + * + * Install a breakpoint + * + * Parameters: + * ef Exception frame + * bp Breakpoint structure for the breakpoint to be installed + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * For hardware breakpoints, a debug register is allocated + * and assigned to the breakpoint. If no debug register is + * available, a warning message is printed and the breakpoint + * is disabled. + * + * For instruction replacement breakpoints, we must single-step + * over the replaced instruction at this point so we can re-install + * the breakpoint instruction after the single-step. + */ + +void +kdba_installbp(kdb_eframe_t ef, kdb_bp_t *bp) +{ + /* + * Install the breakpoint, if it is not already installed. + */ + + if (KDB_DEBUG(BP)) { + kdb_printf("kdba_installbp bp_installed %d\n", bp->bp_installed); + } + if (!bp->bp_installed) { + if (bp->bp_hardtype) { + kdba_installdbreg(bp); + bp->bp_installed = 1; + if (KDB_DEBUG(BP)) { + kdb_printf("kdba_installbp hardware reg %ld at " kdb_bfd_vma_fmt "\n", + bp->bp_hard->bph_reg, bp->bp_addr); + } + } else if (bp->bp_delay) { + if (KDB_DEBUG(BP)) + kdb_printf("kdba_installbp delayed bp\n"); + kdba_handle_bp(ef, bp); + } else { + bp->bp_inst = kdba_getword(bp->bp_addr, 1); + kdba_putword(bp->bp_addr, 1, IA32_BREAKPOINT_INSTRUCTION); + bp->bp_instvalid = 1; + if (KDB_DEBUG(BP)) + kdb_printf("kdba_installbp instruction 0x%x at " kdb_bfd_vma_fmt "\n", + IA32_BREAKPOINT_INSTRUCTION, bp->bp_addr); + bp->bp_installed = 1; + } + } +} + +/* + * kdba_removebp + * + * Make a breakpoint ineffective. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdba_removebp(kdb_bp_t *bp) +{ + /* + * For hardware breakpoints, remove it from the active register, + * for software breakpoints, restore the instruction stream. + */ + if (KDB_DEBUG(BP)) { + kdb_printf("kdba_removebp bp_installed %d\n", bp->bp_installed); + } + if (bp->bp_installed) { + if (bp->bp_hardtype) { + if (KDB_DEBUG(BP)) { + kdb_printf("kdb: removing hardware reg %ld at " kdb_bfd_vma_fmt "\n", + bp->bp_hard->bph_reg, bp->bp_addr); + } + kdba_removedbreg(bp); + } else if (bp->bp_instvalid) { + if (KDB_DEBUG(BP)) + kdb_printf("kdb: restoring instruction 0x%x at " kdb_bfd_vma_fmt "\n", + bp->bp_inst, bp->bp_addr); + kdba_putword(bp->bp_addr, 1, bp->bp_inst); + bp->bp_instvalid = 0; + } + bp->bp_installed = 0; + } +} diff -urN 2.4.0/arch/i386/kdb/kdba_bt.c 2.4.0-ikd-1/arch/i386/kdb/kdba_bt.c --- 2.4.0/arch/i386/kdb/kdba_bt.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/arch/i386/kdb/kdba_bt.c Mon Jan 15 18:40:04 2001 @@ -0,0 +1,348 @@ +/* + * Minimalist Kernel Debugger - Architecture Dependent Stack Traceback + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + * Masahiro Adegawa 1999/12/01 + * 'sr' command, active flag in 'ps' + * Scott Lurndal 1999/12/12 + * Significantly restructure for linux2.3 + * Keith Owens 2000/05/23 + * KDB v1.2 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * bt_print_one + * + * Print one back trace entry. + * + * Inputs: + * ebp Previous frame pointer, 0 if not valid. + * eip Current program counter. + * symtab Information about symbol that eip falls within. + * ar Activation record for this frame. + * argcount Maximum number of arguments to print. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +static void +bt_print_one(kdb_machreg_t eip, kdb_machreg_t ebp, const kdb_ar_t *ar, + const kdb_symtab_t *symtab, int argcount) +{ + int btsymarg = 0; + int nosect = 0; + + kdbgetintenv("BTSYMARG", &btsymarg); + kdbgetintenv("NOSECT", &nosect); + + if (ebp) + kdb_printf("0x%08lx", ebp); + else + kdb_printf(" "); + kdb_symbol_print(eip, symtab, KDB_SP_SPACEB|KDB_SP_VALUE); + if (argcount && ar->args) { + int i, argc = ar->args / 4; + + kdb_printf(" ("); + if (argc > argcount) + argc = argcount; + + for(i=1; i<=argc; i++){ + kdb_machreg_t argp = ar->arg0 - ar->args + 4*i; + + if (i != 1) + kdb_printf(", "); + kdb_printf("0x%lx", + kdba_getword(argp, sizeof(kdb_machreg_t))); + } + kdb_printf(")"); + } + if (symtab->sym_name) { + if (!nosect) { + kdb_printf("\n"); + kdb_printf(" %s %s 0x%lx 0x%lx 0x%lx", + symtab->mod_name, + symtab->sec_name, + symtab->sec_start, + symtab->sym_start, + symtab->sym_end); + } + } + kdb_printf("\n"); + if (argcount && ar->args && btsymarg) { + int i, argc = ar->args / 4; + kdb_symtab_t arg_symtab; + kdb_machreg_t arg; + for(i=1; i<=argc; i++){ + kdb_machreg_t argp = ar->arg0 - ar->args + 4*i; + arg = kdba_getword(argp, sizeof(kdb_machreg_t)); + if (kdbnearsym(arg, &arg_symtab)) { + kdb_printf(" "); + kdb_symbol_print(arg, &arg_symtab, KDB_SP_DEFAULT|KDB_SP_NEWLINE); + } + } + } +} + +/* + * kdba_bt_stack_i386 + * + * kdba_bt_stack with i386 specific parameters. + * Specification as kdba_bt_stack plus :- + * + * Inputs: + * As kba_bt_stack plus + * regs_esp If 1 get esp from the registers (exception frame), if 0 + * get esp from kdba_getregcontents. + */ + +static int +kdba_bt_stack_i386(struct pt_regs *regs, kdb_machreg_t *addr, int argcount, + struct task_struct *p, int regs_esp) +{ + kdb_ar_t ar; + kdb_machreg_t eip, esp, ebp, ss, cs; + kdb_symtab_t symtab; + + /* + * The caller may have supplied an address at which the + * stack traceback operation should begin. This address + * is assumed by this code to point to a return-address + * on the stack to be traced back. + * + * The end result of this will make it appear as if a function + * entitled '' was called from the function which + * contains return-address. + */ + if (addr) { + eip = 0; + ebp = 0; + esp = *addr; + cs = __KERNEL_CS; /* have to assume kernel space */ + } else { + eip = regs->eip; + ebp = regs->ebp; + if (regs_esp) + esp = regs->esp; + else + kdba_getregcontents("esp", regs, &esp); + kdba_getregcontents("xcs", regs, &cs); + } + ss = esp & -8192; + + if ((cs & 0xffff) != __KERNEL_CS) { + kdb_printf("Stack is not in kernel space, backtrace not available\n"); + return 0; + } + + kdb_printf(" EBP EIP Function(args)\n"); + + /* + * Run through the activation records and print them. + */ + + while (1) { + kdbnearsym(eip, &symtab); + if (!kdb_get_next_ar(esp, symtab.sym_start, eip, ebp, ss, + &ar, &symtab)) { + break; + } + + if (strcmp(".text.lock", symtab.sec_name) == 0) { + /* + * Instructions in the .text.lock area are generated by + * the out of line code in lock handling, see + * include/asm-i386 semaphore.h and rwlock.h. There can + * be multiple instructions which eventually end with a + * jump back to the mainline code. Use the disassmebler + * to silently step through the code until we find the + * jump, resolve its destination and translate it to a + * symbol. Replace '.text.lock' with the symbol. + */ + unsigned char inst; + kdb_machreg_t offset = 0, realeip = eip; + int length, offsize = 0; + kdb_symtab_t lock_symtab; + /* Dummy out the disassembler print function */ + fprintf_ftype save_fprintf_func = kdb_di.fprintf_func; + + kdb_di.fprintf_func = &kdb_dis_fprintf_dummy; + while((length = kdba_id_printinsn(realeip, &kdb_di)) > 0) { + inst = kdba_getword(realeip, 1); + offsize = 0; + switch (inst) { + case 0xeb: /* jmp with 1 byte offset */ + offsize = 1; + offset = kdba_getword(realeip+1, offsize); + break; + case 0xe9: /* jmp with 4 byte offset */ + offsize = 4; + offset = kdba_getword(realeip+1, offsize); + break; + default: + realeip += length; /* next instruction */ + break; + } + if (offsize) + break; + } + kdb_di.fprintf_func = save_fprintf_func; + + if (offsize) { + realeip += 1 + offsize + offset; + if (kdbnearsym(realeip, &lock_symtab)) { + /* Print the stext entry without args */ + bt_print_one(eip, 0, &ar, &symtab, 0); + /* Point to mainline code */ + eip = realeip; + continue; + } + } + } + + if (strcmp("ret_from_intr", symtab.sym_name) == 0) { + /* + * Non-standard frame. ret_from_intr is preceded by + * 9 registers (ebx, ecx, edx, esi, edi, ebp, eax, ds, + * cs), original eax and the return address for a total + * of 11 words. + */ + ar.start = ar.end + 11*4; + /* Print the ret_from_intr entry without args */ + bt_print_one(eip, 0, &ar, &symtab, 0); + kdb_printf("Interrupt registers:\n"); + kdba_dumpregs((struct pt_regs *)(ar.end), NULL, NULL); + /* Step the frame to the interrupted code */ + eip = kdba_getword(ar.start-4, 4); + ebp = 0; + esp = ar.start; + continue; + } + + if (strcmp("error_code", symtab.sym_name) == 0) { + /* + * Non-standard frame. error_code is preceded + * by two parameters (-> registers, error code), + * 9 registers (ebx, ecx, edx, esi, edi, ebp, eax, ds, + * cs), original eax and the return address for a total + * of 13 words. + */ + ar.start = ar.end + 13*4; + /* Print the error_code entry without args */ + bt_print_one(eip, 0, &ar, &symtab, 0); + kdb_printf("Interrupt registers:\n"); + kdba_dumpregs((struct pt_regs *)(ar.end+8), NULL, NULL); + /* Step the frame to the interrupted code */ + eip = kdba_getword(ar.start-4, 4); + ebp = 0; + esp = ar.start; + continue; + } + + bt_print_one(eip, ebp, &ar, &symtab, argcount); + + if (ar.ret == 0) + break; /* End of frames */ + eip = ar.ret; + ebp = ar.oldfp; + esp = ar.start; + } + + return 0; +} + +/* + * kdba_bt_stack + * + * This function implements the 'bt' command. Print a stack + * traceback. + * + * bt [] (addr-exp is for alternate stacks) + * btp (Kernel stack for ) + * + * address expression refers to a return address on the stack. It + * may be preceeded by a frame pointer. + * + * Inputs: + * regs registers at time kdb was entered. + * addr Pointer to Address provided to 'bt' command, if any. + * argcount + * p Pointer to task for 'btp' command. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * mds comes in handy when examining the stack to do a manual + * traceback. + */ + +int +kdba_bt_stack(struct pt_regs *regs, kdb_machreg_t *addr, int argcount, + struct task_struct *p) +{ + return(kdba_bt_stack_i386(regs, addr, argcount, p, 0)); +} + +int +kdba_bt_process(struct task_struct *p, int argcount) +{ + struct pt_regs taskregs; + + memset(&taskregs, 0, sizeof(taskregs)); + taskregs.eip = p->thread.eip; + taskregs.esp = p->thread.esp; + + /* + * Since we don't really use the TSS + * to store register between task switches, + * attempt to locate real ebp (should be + * top of stack if task is in schedule) + */ + taskregs.ebp = *(kdb_machreg_t *)(taskregs.esp); + + taskregs.xcs = __KERNEL_CS; /* have to assume kernel space */ + + if (taskregs.esp < (unsigned long)p || + taskregs.esp >= (unsigned long)p + 8192) { + kdb_printf("Stack is not in task_struct, backtrace not available\n"); + return(0); + } + + return kdba_bt_stack_i386(&taskregs, NULL, argcount, p, 1); + +} diff -urN 2.4.0/arch/i386/kdb/kdba_id.c 2.4.0-ikd-1/arch/i386/kdb/kdba_id.c --- 2.4.0/arch/i386/kdb/kdba_id.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/arch/i386/kdb/kdba_id.c Mon Jan 15 18:40:04 2001 @@ -0,0 +1,270 @@ +/* + * Minimalist Kernel Debugger - Architecture Dependent Instruction Disassembly + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + * Masahiro Adegawa 1999/12/01 + * 'sr' command, active flag in 'ps' + * Scott Lurndal 1999/12/12 + * Significantly restructure for linux2.3 + * Keith Owens 2000/05/23 + * KDB v1.2 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * kdba_dis_getsym + * + * Get a symbol for the disassembler. + * + * Parameters: + * addr Address for which to get symbol + * dip Pointer to disassemble_info + * Returns: + * 0 + * Locking: + * Remarks: + * Not used for kdb. + */ + +/* ARGSUSED */ +static int +kdba_dis_getsym(bfd_vma addr, disassemble_info *dip) +{ + + return 0; +} + +/* + * kdba_printaddress + * + * Print (symbolically) an address. + * + * Parameters: + * addr Address for which to get symbol + * dip Pointer to disassemble_info + * flag True if a ":" sequence should follow the address + * Returns: + * 0 + * Locking: + * Remarks: + * + */ + +/* ARGSUSED */ +void +kdba_printaddress(kdb_machreg_t addr, disassemble_info *dip, int flag) +{ + kdb_symtab_t symtab; + + /* + * Print a symbol name or address as necessary. + */ + kdbnearsym(addr, &symtab); + if (symtab.sym_name) { + /* Do not use kdb_symbol_print here, it always does + * kdb_printf but we want dip->fprintf_func. + */ + dip->fprintf_func(dip->stream, + "0x%0*lx %s", + 2*sizeof(addr), addr, symtab.sym_name); + if (addr != symtab.sym_start) + dip->fprintf_func(dip->stream, "+0x%x", addr - symtab.sym_start); + + } else { + dip->fprintf_func(dip->stream, "0x%x", addr); + } + + if (flag) + dip->fprintf_func(dip->stream, ": "); +} + +/* + * kdba_dis_printaddr + * + * Print (symbolically) an address. Called by GNU disassembly + * code via disassemble_info structure. + * + * Parameters: + * addr Address for which to get symbol + * dip Pointer to disassemble_info + * Returns: + * 0 + * Locking: + * Remarks: + * This function will never append ":" to the printed + * symbolic address. + */ + +static void +kdba_dis_printaddr(bfd_vma addr, disassemble_info *dip) +{ + kdba_printaddress(addr, dip, 0); +} + +/* + * kdba_dis_getmem + * + * Fetch 'length' bytes from 'addr' into 'buf'. + * + * Parameters: + * addr Address for which to get symbol + * buf Address of buffer to fill with bytes from 'addr' + * length Number of bytes to fetch + * dip Pointer to disassemble_info + * Returns: + * 0 + * Locking: + * Remarks: + * + */ + +/* ARGSUSED */ +static int +kdba_dis_getmem(bfd_vma addr, bfd_byte *buf, unsigned int length, disassemble_info *dip) +{ + bfd_byte *bp = buf; + int i; + + /* + * Fill the provided buffer with bytes from + * memory, starting at address 'addr' for 'length bytes. + * + */ + + for(i=0; imach = bfd_mach_i386_i386; + } else if (strcmp(mode, "8086") == 0) { + dip->mach = bfd_mach_i386_i8086; + } else { + return KDB_BADMODE; + } + } + + return 0; +} + +/* + * kdba_check_pc + * + * Check that the pc is satisfactory. + * + * Parameters: + * pc Program Counter Value. + * Returns: + * None + * Locking: + * None. + * Remarks: + * Can change pc. + */ + +void +kdba_check_pc(kdb_machreg_t *pc) +{ + /* No action */ +} + +/* + * kdba_id_printinsn + * + * Format and print a single instruction at 'pc'. Return the + * length of the instruction. + * + * Parameters: + * pc Program Counter Value. + * dip Disassemble_info structure pointer + * Returns: + * Length of instruction, -1 for error. + * Locking: + * None. + * Remarks: + * Depends on 'IDMODE' environment variable. + */ + +int +kdba_id_printinsn(kdb_machreg_t pc, disassemble_info *dip) +{ + kdba_dis_printaddr(pc, dip); + return print_insn_i386_att(pc, dip); +} + +/* + * kdba_id_init + * + * Initialize the architecture dependent elements of + * the disassembly information structure + * for the GNU disassembler. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void __init +kdba_id_init(disassemble_info *dip) +{ + dip->read_memory_func = kdba_dis_getmem; + dip->print_address_func = kdba_dis_printaddr; + dip->symbol_at_address_func = kdba_dis_getsym; + + dip->flavour = bfd_target_elf_flavour; + dip->arch = bfd_arch_i386; + dip->mach = bfd_mach_i386_i386; + dip->endian = BFD_ENDIAN_LITTLE; + + dip->display_endian = BFD_ENDIAN_LITTLE; +} diff -urN 2.4.0/arch/i386/kdb/kdba_io.c 2.4.0-ikd-1/arch/i386/kdb/kdba_io.c --- 2.4.0/arch/i386/kdb/kdba_io.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/arch/i386/kdb/kdba_io.c Mon Jan 15 18:40:04 2001 @@ -0,0 +1,368 @@ +/* + * Kernel Debugger Console I/O handler + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Chuck Fleckenstein 1999/07/20 + * Move kdb_info struct declaration to this file + * for cases where serial support is not compiled into + * the kernel. + * + * Masahiro Adegawa 1999/07/20 + * Handle some peculiarities of japanese 86/106 + * keyboards. + * + * marc@mucom.co.il 1999/07/20 + * Catch buffer overflow for serial input. + * + * Scott Foehner + * Port to ia64 + * + * Scott Lurndal 2000/01/03 + * Restructure for v1.0 + * + * Keith Owens 2000/05/23 + * KDB v1.2 + * + * Andi Kleen 2000/03/19 + * Support simultaneous input from serial line and keyboard. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define KDB_BLINK_LED 1 + +int kdb_port; + +/* + * This module contains code to read characters from the keyboard or a serial + * port. + * + * It is used by the kernel debugger, and is polled, not interrupt driven. + * + */ + +/* + * send: Send a byte to the keyboard controller. Used primarily to + * alter LED settings. + */ + +static void +kdb_kbdsend(unsigned char byte) +{ + while (inb(KBD_STATUS_REG) & KBD_STAT_IBF) + ; + outb(byte, KBD_DATA_REG); +} + +static void +kdb_toggleled(int led) +{ + static int leds; + + leds ^= led; + + kdb_kbdsend(KBD_CMD_SET_LEDS); + kdb_kbdsend((unsigned char)leds); +} + +void +kdb_resetkeyboard(void) +{ +#if 0 + kdb_kbdsend(KBD_CMD_ENABLE); +#endif +} + +#if defined(CONFIG_SERIAL_CONSOLE) +/* Check if there is a byte ready at the serial port */ +static int get_serial_char(void) +{ + unsigned char ch; + int status; +#define serial_inp(info, offset) inb((info) + (offset)) +#define serial_out(info, offset, v) outb((v), (info) + (offset)) + + if (kdb_port == 0) + return -1; + + if ((status = serial_inp(kdb_port, UART_LSR)) & UART_LSR_DR) { + ch = serial_inp(kdb_port, UART_RX); + if (ch == 0x7f) + ch = 8; + if (ch == '\t') + ch = ' '; + if (ch == 8) { /* BS */ + ; + } else if (ch == 13) { /* Enter */ + kdb_printf("\n"); + } else { + if (!isprint(ch)) + return(-1); + kdb_printf("%c", ch); + } + return ch; + } + return -1; +} +#endif /* CONFIG_SERIAL_CONSOLE */ + +#if defined(CONFIG_VT) +/* + * Check if the keyboard controller has a keypress for us. + * Some parts (Enter Release, LED change) are still blocking polled here, + * but hopefully they are all short. + */ +static int get_kbd_char(void) +{ + int scancode, scanstatus; + static int shift_lock; /* CAPS LOCK state (0-off, 1-on) */ + static int shift_key; /* Shift next keypress */ + static int ctrl_key; + u_short keychar; + extern u_short plain_map[], shift_map[], ctrl_map[]; + + if ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0) + return -1; + + /* + * Fetch the scancode + */ + scancode = inb(KBD_DATA_REG); + scanstatus = inb(KBD_STATUS_REG); + + /* + * Ignore mouse events. + */ + if (scanstatus & KBD_STAT_MOUSE_OBF) + return -1; + + /* + * Ignore release, trigger on make + * (except for shift keys, where we want to + * keep the shift state so long as the key is + * held down). + */ + + if (((scancode&0x7f) == 0x2a) || ((scancode&0x7f) == 0x36)) { + /* + * Next key may use shift table + */ + if ((scancode & 0x80) == 0) { + shift_key=1; + } else { + shift_key=0; + } + return -1; + } + + if ((scancode&0x7f) == 0x1d) { + /* + * Left ctrl key + */ + if ((scancode & 0x80) == 0) { + ctrl_key = 1; + } else { + ctrl_key = 0; + } + return -1; + } + + if ((scancode & 0x80) != 0) + return -1; + + scancode &= 0x7f; + + /* + * Translate scancode + */ + + if (scancode == 0x3a) { + /* + * Toggle caps lock + */ + shift_lock ^= 1; + + kdb_toggleled(0x4); + return -1; + } + + if (scancode == 0x0e) { + /* + * Backspace + */ + return 8; + } + + if (scancode == 0xe0) { + return -1; + } + + /* + * For Japanese 86/106 keyboards + * See comment in drivers/char/pc_keyb.c. + * - Masahiro Adegawa + */ + if (scancode == 0x73) { + scancode = 0x59; + } else if (scancode == 0x7d) { + scancode = 0x7c; + } + + if (!shift_lock && !shift_key && !ctrl_key) { + keychar = plain_map[scancode]; + } else if (shift_lock || shift_key) { + keychar = shift_map[scancode]; + } else if (ctrl_key) { + keychar = ctrl_map[scancode]; + } else { + keychar = 0x0020; + kdb_printf("Unknown state/scancode (%d)\n", scancode); + } + keychar &= 0x0fff; + if (keychar == '\t') + keychar = ' '; + switch (KTYP(keychar)) { + case KT_LETTER: + case KT_LATIN: + if (isprint(keychar)) + break; /* printable characters */ + /* drop through */ + case KT_SPEC: + if (keychar == K_ENTER) + break; + /* drop through */ + default: + return(-1); /* ignore unprintables */ + } + + if ((scancode & 0x7f) == 0x1c) { + /* + * enter key. All done. Absorb the release scancode. + */ + while ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0) + ; + + /* + * Fetch the scancode + */ + scancode = inb(KBD_DATA_REG); + scanstatus = inb(KBD_STATUS_REG); + + while (scanstatus & KBD_STAT_MOUSE_OBF) { + scancode = inb(KBD_DATA_REG); + scanstatus = inb(KBD_STATUS_REG); + } + + if (scancode != 0x9c) { + /* + * Wasn't an enter-release, why not? + */ + kdb_printf("kdb: expected enter got 0x%x status 0x%x\n", + scancode, scanstatus); + } + + kdb_printf("\n"); + return 13; + } + + /* + * echo the character. + */ + kdb_printf("%c", keychar&0xff); + + return keychar & 0xff; +} +#endif /* CONFIG_VT */ + +#ifdef KDB_BLINK_LED + +/* Leave numlock alone, setting it messes up laptop keyboards with the keypad + * mapped over normal keys. + */ +int kdba_blink_mask = 0x1 | 0x4; + +#define BOGOMIPS ((boot_cpu_data.loops_per_jiffy/(5000/HZ)) % 100) +static int blink_led(void) +{ + static long delay; + if (--delay < 0) { + if (BOGOMIPS == 0) /* early kdb */ + delay = 150000000/1000; /* arbitrary bogomips */ + else + delay = 150000000/BOGOMIPS; /* Roughly 1 second when polling */ + kdb_toggleled(kdba_blink_mask); + } + return -1; +} +#endif + +typedef int (*get_char_func)(void); + +static get_char_func poll_funcs[] = { +#if defined(CONFIG_VT) + get_kbd_char, +#endif +#if defined(CONFIG_SERIAL_CONSOLE) + get_serial_char, +#endif +#ifdef KDB_BLINK_LED + blink_led, +#endif + NULL +}; + +char * +kdba_read(char *buffer, size_t bufsize) +{ + char *cp = buffer; + char *bufend = buffer+bufsize-2; /* Reserve space for newline and null byte */ + + for (;;) { + int key; + get_char_func *f; + for (f = &poll_funcs[0]; ; ++f) { + if (*f == NULL) + f = &poll_funcs[0]; + key = (*f)(); + if (key != -1) + break; + } + + /* Echo is done in the low level functions */ + switch (key) { + case 8: /* backspace */ + if (cp > buffer) { + kdb_printf("\b \b"); + --cp; + } + break; + case 13: /* enter */ + *cp++ = '\n'; + *cp++ = '\0'; + return buffer; + default: + if (cp < bufend) + *cp++ = key; + break; + } + } +} diff -urN 2.4.0/arch/i386/kdb/kdbasupport.c 2.4.0-ikd-1/arch/i386/kdb/kdbasupport.c --- 2.4.0/arch/i386/kdb/kdbasupport.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/arch/i386/kdb/kdbasupport.c Mon Jan 15 18:40:04 2001 @@ -0,0 +1,1609 @@ +/* + * Kernel Debugger Architecture Independent Support Functions + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +char *kdb_diemsg; + + +/* + * kdba_find_return + * + * Given a starting point on the stack and symtab data for the + * current function, scan up the stack looking for a return + * address for this function. + * Inputs: + * sp Starting stack pointer for scan + * ss Start of stack for current process + * symtab kallsyms symbol data for the function + * Outputs: + * None. + * Returns: + * Position on stack of return address, 0 if not found. + * Locking: + * None. + * Remarks: + * This is sensitive to the calling sequence generated by gcc. + */ + +static kdb_machreg_t +kdba_find_return(kdb_machreg_t sp, kdb_machreg_t ss, const kdb_symtab_t *symtab) +{ + kdb_machreg_t ret; + kdb_symtab_t caller_symtab; + + if (KDB_DEBUG(ARA)) { + kdb_printf(" kdba_find_return: start\n"); + } + + if ((sp & -8192) != ss) { + kdb_printf(" sp is in wrong stack 0x%lx 0x%lx 0x%lx\n", sp, ss, sp & -8192); + return(0); + } + + if ((sp & (8192 - 1)) < sizeof(struct task_struct)) { + kdb_printf(" sp is inside task_struct\n"); + return(0); + } + + for (;ret = 0, sp & (8192-1);sp += 4) { + if (KDB_DEBUG(ARA)) { + kdb_printf(" sp=0x%lx", sp); + } + ret = kdba_getword(sp, 4); + kdbnearsym(ret, &caller_symtab); + if (KDB_DEBUG(ARA)) { + kdb_printf(" ret="); + kdb_symbol_print(ret, &caller_symtab, KDB_SP_DEFAULT|KDB_SP_SYMSIZE); + } + if (!caller_symtab.sym_name) { + if (KDB_DEBUG(ARA)) { + kdb_printf("\n"); + } + continue; /* not a valid kernel address */ + } + if (kdba_getword(ret-5, 1) == 0xe8) { + /* call disp32 */ + if (KDB_DEBUG(ARA)) { + kdb_printf(" call disp32"); + } + if (ret + kdba_getword(ret-4, 4) == symtab->sym_start) { + if (KDB_DEBUG(ARA)) { + kdb_printf(" matched\n"); + } + break; /* call to this function */ + } + if (KDB_DEBUG(ARA)) { + kdb_printf(" failed"); + } + } else if (kdba_getword(ret-7, 1) == 0xff && + kdba_getword(ret-6, 1) == 0x14 && + kdba_getword(ret-5, 1) == 0x85) { + /* call *0xnnnn(,%eax,4), used by syscall. + * Cannot calculate address, assume it is valid + * if the current function name starts with + * 'sys_' or 'old_'. + */ + if (KDB_DEBUG(ARA)) { + kdb_printf(" call *0xnnnn(,%%eax,4)"); + } + if (strncmp(symtab->sym_name, "sys_", 4) == 0 || + strncmp(symtab->sym_name, "old_", 4) == 0) { + if (KDB_DEBUG(ARA)) { + kdb_printf(" matched\n"); + } + break; /* probably call to this function */ + } + if (KDB_DEBUG(ARA)) { + kdb_printf(" failed"); + } + } else if (kdba_getword(ret-2, 1) == 0xff && + (kdba_getword(ret-1, 1) & 0xf8) == 0xd0) { + /* call *%reg. Cannot validate, have to assume + * it is valid. + */ + if (KDB_DEBUG(ARA)) { + kdb_printf(" call *%%reg, assume valid\n"); + } + break; /* hope it is a call to this function */ + } else if (kdba_getword(ret-5, 1) == 0xe9) { + /* jmp disp32. I have been told that gcc may + * do function tail optimization and replace + * call with jmp. + */ + if (KDB_DEBUG(ARA)) { + kdb_printf(" jmp disp32\n"); + } + if (ret + kdba_getword(ret-4, 4) == symtab->sym_start) { + if (KDB_DEBUG(ARA)) { + kdb_printf(" matched\n"); + } + break; /* jmp to this function */ + } + if (KDB_DEBUG(ARA)) { + kdb_printf(" failed"); + } + } else if (kdba_getword(ret-2, 1) == 0xeb) { + /* jmp disp8 */ + if (KDB_DEBUG(ARA)) { + kdb_printf(" jmp disp8\n"); + } + if (ret + kdba_getword(ret-1, 1) == symtab->sym_start) { + if (KDB_DEBUG(ARA)) { + kdb_printf(" matched\n"); + } + break; /* jmp to this function */ + } + if (KDB_DEBUG(ARA)) { + kdb_printf(" failed"); + } + } else if (strcmp(caller_symtab.sym_name, "ret_from_intr") == 0 + && ret == caller_symtab.sym_start) { + /* ret_from_intr is pushed on stack for interrupts */ + if (KDB_DEBUG(ARA)) { + kdb_printf(" ret_from_intr matched\n"); + } + break; /* special case, hand crafted frame */ + } + if (KDB_DEBUG(ARA)) { + kdb_printf("\n"); + } + } + if (KDB_DEBUG(ARA)) { + kdb_printf(" end ret=0x%lx sp=0x%lx\n", ret, sp); + } + if (ret) + return(sp); + return(0); +} + +/* + * kdba_prologue + * + * This function analyzes a gcc-generated function prototype + * with or without frame pointers to determine the amount of + * automatic storage and register save storage is used on the + * stack of the target function. It only counts instructions + * that have been executed up to but excluding the current eip. + * Inputs: + * code Start address of function code to analyze + * pc Current program counter within function + * sp Current stack pointer for function + * fp Current frame pointer for function, may not be valid + * ss Start of stack for current process. + * caller 1 if looking for data on the caller frame, 0 for callee. + * Outputs: + * ar Activation record, all fields may be set. fp and oldfp + * are 0 if they cannot be extracted. return is 0 if the + * code cannot find a valid return address. args and arg0 + * are 0 if the number of arguments cannot be safely + * calculated. + * Returns: + * 1 if prologue is valid, 0 otherwise. If pc is 0 treat it as a + * valid prologue to allow bt on wild branches. + * Locking: + * None. + * Remarks: + * + * A prologue for ia32 generally looks like: + * + * pushl %ebp [All functions, but only if + * movl %esp, %ebp compiled with frame pointers] + * subl $auto, %esp [some functions] + * pushl %reg [some functions] + * pushl %reg [some functions] + * + * FIXME: Mike Galbraith says that gcc 2.95 can generate a slightly + * different prologue. No support for gcc 2.95 yet. + */ + +int +kdba_prologue(const kdb_symtab_t *symtab, kdb_machreg_t pc, kdb_machreg_t sp, + kdb_machreg_t fp, kdb_machreg_t ss, int caller, kdb_ar_t *ar) +{ + kdb_machreg_t ret_p, code = symtab->sym_start; + int oldfp_present = 0, unwound = 0; + + if (KDB_DEBUG(ARA)) { + kdb_printf("kdba_prologue: code=0x%lx %s pc=0x%lx sp=0x%lx fp=0x%lx\n", + code, symtab->sym_name, pc, sp, fp); + } + + /* Special case for wild branches. Assumes top of stack is return address */ + if (pc == 0) { + memset(ar, 0, sizeof(*ar)); + ar->setup = 4; + ar->end = sp; + ar->start = ar->end + 4; + ar->ret = kdba_getword(sp, 4); + if (KDB_DEBUG(ARA)) { + kdb_printf(" pc==0: ret=0x%lx\n", ar->ret); + } + return(1); + } + + if (code == 0 || sp & 3 || ss != (sp & -8192)) + return(0); + + ar->end = sp; /* End of activation record +1 */ + + /* Special cases galore when the caller pc is within entry.S. + * The return address for these routines is outside the kernel, + * so the normal algorithm to find the frame does not work. + * Hand craft the frame to no setup, regs, locals etc, assume 6 + * parameters. + * This list was extracted from entry.S by looking for all call + * instructions that were eventually followed by RESTORE_ALL, + * take the label before each such instruction. + */ + if (caller && + (strcmp(symtab->sym_name, "lcall7") == 0 || + strcmp(symtab->sym_name, "lcall27") == 0 || + strcmp(symtab->sym_name, "kdb_call") == 0 || + strcmp(symtab->sym_name, "system_call") == 0 || + strcmp(symtab->sym_name, "tracesys") == 0 || + strcmp(symtab->sym_name, "signal_return") == 0 || + strcmp(symtab->sym_name, "v86_signal_return") == 0 || + strcmp(symtab->sym_name, "tracesys") == 0 || + strcmp(symtab->sym_name, "tracesys_exit") == 0 || + strcmp(symtab->sym_name, "handle_softirq") == 0 || + strcmp(symtab->sym_name, "reschedule") == 0 || + strcmp(symtab->sym_name, "error_code") == 0 || + strcmp(symtab->sym_name, "device_not_available") == 0 || + strcmp(symtab->sym_name, "nmi") == 0)) { + ar->start = ar->end + 6*4; /* 6 parameters */ + if ((ar->start & -8192) != ss) + ar->start = 0; + return(1); + } + + ar->setup = 4; /* Return address is always on stack */ + + /* Kludge. If we are sitting on 'ret' then the stack has been unwound, + * ignore all the startup code. + */ + if (kdba_getword(pc, 1) == 0xc3) { + /* ret */ + unwound = 1; + } + + if (!unwound + && code < pc + && kdba_getword(code, 1) == 0x55) { + /* pushl %ebp */ + ar->setup += 4; /* Frame pointer is on stack */ + oldfp_present = 1; + ++code; + if (KDB_DEBUG(ARA)) { + kdb_printf(" pushl %%ebp\n"); + } + if (code < pc && + kdba_getword(code, 1) == 0x89 && + kdba_getword(code+1, 1) == 0xe5) { + /* movl %esp,%ebp */ + if (fp >= sp && (fp & -8192) == ss) + ar->fp = fp; /* %ebp has been set */ + code += 2; + if (KDB_DEBUG(ARA)) { + kdb_printf(" movl %%esp,%%ebp, fp=0x%lx\n", ar->fp); + } + } + } + + if (!unwound && code < pc) { + if (kdba_getword(code, 1) == 0x83 && + kdba_getword(code+1, 1) == 0xec) { + /* subl $xx,%esp */ + code += 2; + ar->locals = kdba_getword(code, 1); + ++code; + if (KDB_DEBUG(ARA)) { + kdb_printf(" subl $xx,%%esp, locals=%d\n", ar->locals); + } + } else if (kdba_getword(code, 1) == 0x81 && + kdba_getword(code+1, 1) == 0xec) { + /* subl $xxxxxxxx,%esp */ + code += 2; + ar->locals = kdba_getword(code, 4); + code += 4; + if (KDB_DEBUG(ARA)) { + kdb_printf(" subl $xxxxxxxx,%%esp, locals=%d\n", ar->locals); + } + } + } + + while (!unwound && code < pc && (kdba_getword(code, 1)&0xf8) == 0x50) { + /* pushl %reg */ + ar->regs += 4; + ++code; + if (KDB_DEBUG(ARA)) { + kdb_printf(" pushl %%reg, regs=%d\n", ar->regs); + } + } + + /* Check the return address. It must point within the kernel + * and the code at that location must be a valid entry sequence. + */ + if (ar->fp) { + ret_p = ar->fp + ar->setup; + } + else { + ret_p = ar->end + ar->regs + ar->locals + ar->setup; + } + ret_p -= 4; + if (KDB_DEBUG(ARA)) { + kdb_printf(" ret_p(0)=0x%lx\n", ret_p); + } + if ((ret_p & -8192) == ss && + (ret_p = kdba_find_return(ret_p, ss, symtab))) { + ar->ret = kdba_getword(ret_p, 4); + } + if (KDB_DEBUG(ARA)) { + kdb_printf(" ret_p(1)=0x%lx ret=0x%lx\n", ret_p, ar->ret); + } + if (ar->ret) { + ar->fp = ret_p - ar->setup + 4; /* "accurate" fp */ + ar->start = ret_p + 4; + if (KDB_DEBUG(ARA)) { + kdb_printf(" fp=0x%lx start=0x%lx\n", ar->fp, ar->start); + } + } + if (oldfp_present) { + if (ar->fp) + ar->oldfp = kdba_getword(ar->fp, 4); + if (KDB_DEBUG(ARA)) { + kdb_printf(" oldfp=0x%lx", ar->oldfp); + } + if (ar->oldfp <= ar->fp || (ar->oldfp & -8192) != ss) { + ar->oldfp = 0; + if (KDB_DEBUG(ARA)) { + kdb_printf(" (out of range)"); + } + } + if (KDB_DEBUG(ARA)) { + kdb_printf("\n"); + } + } + return(1); +} + +void +kdba_installdbreg(kdb_bp_t *bp) +{ + kdb_machreg_t dr7; + + dr7 = kdb_getdr7(); + + kdb_putdr(bp->bp_hard->bph_reg, bp->bp_addr); + + dr7 |= DR7_GE; + + switch (bp->bp_hard->bph_reg){ + case 0: + DR7_RW0SET(dr7,bp->bp_hard->bph_mode); + DR7_LEN0SET(dr7,bp->bp_hard->bph_length); + DR7_G0SET(dr7); + break; + case 1: + DR7_RW1SET(dr7,bp->bp_hard->bph_mode); + DR7_LEN1SET(dr7,bp->bp_hard->bph_length); + DR7_G1SET(dr7); + break; + case 2: + DR7_RW2SET(dr7,bp->bp_hard->bph_mode); + DR7_LEN2SET(dr7,bp->bp_hard->bph_length); + DR7_G2SET(dr7); + break; + case 3: + DR7_RW3SET(dr7,bp->bp_hard->bph_mode); + DR7_LEN3SET(dr7,bp->bp_hard->bph_length); + DR7_G3SET(dr7); + break; + default: + kdb_printf("kdb: Bad debug register!! %ld\n", + bp->bp_hard->bph_reg); + break; + } + + kdb_putdr7(dr7); + return; +} + +void +kdba_removedbreg(kdb_bp_t *bp) +{ + int regnum; + kdb_machreg_t dr7; + + if (!bp->bp_hard) + return; + + regnum = bp->bp_hard->bph_reg; + + dr7 = kdb_getdr7(); + + kdb_putdr(regnum, 0); + + switch (regnum) { + case 0: + DR7_G0CLR(dr7); + DR7_L0CLR(dr7); + break; + case 1: + DR7_G1CLR(dr7); + DR7_L1CLR(dr7); + break; + case 2: + DR7_G2CLR(dr7); + DR7_L2CLR(dr7); + break; + case 3: + DR7_G3CLR(dr7); + DR7_L3CLR(dr7); + break; + default: + kdb_printf("kdb: Bad debug register!! %d\n", regnum); + break; + } + + kdb_putdr7(dr7); +} + +kdb_machreg_t +kdb_getdr6(void) +{ + return kdb_getdr(6); +} + +kdb_machreg_t +kdb_getdr7(void) +{ + return kdb_getdr(7); +} + +kdb_machreg_t +kdb_getdr(int regnum) +{ + kdb_machreg_t contents = 0; + switch(regnum) { + case 0: + __asm__ ("movl %%db0,%0\n\t":"=r"(contents)); + break; + case 1: + __asm__ ("movl %%db1,%0\n\t":"=r"(contents)); + break; + case 2: + __asm__ ("movl %%db2,%0\n\t":"=r"(contents)); + break; + case 3: + __asm__ ("movl %%db3,%0\n\t":"=r"(contents)); + break; + case 4: + case 5: + break; + case 6: + __asm__ ("movl %%db6,%0\n\t":"=r"(contents)); + break; + case 7: + __asm__ ("movl %%db7,%0\n\t":"=r"(contents)); + break; + default: + break; + } + + return contents; +} + + +kdb_machreg_t +kdb_getcr(int regnum) +{ + kdb_machreg_t contents = 0; + switch(regnum) { + case 0: + __asm__ ("movl %%cr0,%0\n\t":"=r"(contents)); + break; + case 1: + break; + case 2: + __asm__ ("movl %%cr2,%0\n\t":"=r"(contents)); + break; + case 3: + __asm__ ("movl %%cr3,%0\n\t":"=r"(contents)); + break; + case 4: + __asm__ ("movl %%cr4,%0\n\t":"=r"(contents)); + break; + default: + break; + } + + return contents; +} + +void +kdb_putdr6(kdb_machreg_t contents) +{ + kdb_putdr(6, contents); +} + +void +kdb_putdr7(kdb_machreg_t contents) +{ + kdb_putdr(7, contents); +} + +void +kdb_putdr(int regnum, kdb_machreg_t contents) +{ + switch(regnum) { + case 0: + __asm__ ("movl %0,%%db0\n\t"::"r"(contents)); + break; + case 1: + __asm__ ("movl %0,%%db1\n\t"::"r"(contents)); + break; + case 2: + __asm__ ("movl %0,%%db2\n\t"::"r"(contents)); + break; + case 3: + __asm__ ("movl %0,%%db3\n\t"::"r"(contents)); + break; + case 4: + case 5: + break; + case 6: + __asm__ ("movl %0,%%db6\n\t"::"r"(contents)); + break; + case 7: + __asm__ ("movl %0,%%db7\n\t"::"r"(contents)); + break; + default: + break; + } +} + +/* + * kdba_getregcontents + * + * Return the contents of the register specified by the + * input string argument. Return an error if the string + * does not match a machine register. + * + * The following pseudo register names are supported: + * ®s - Prints address of exception frame + * kesp - Prints kernel stack pointer at time of fault + * cesp - Prints current kernel stack pointer, inside kdb + * ceflags - Prints current flags, inside kdb + * % - Uses the value of the registers at the + * last time the user process entered kernel + * mode, instead of the registers at the time + * kdb was entered. + * + * Parameters: + * regname Pointer to string naming register + * regs Pointer to structure containing registers. + * Outputs: + * *contents Pointer to unsigned long to recieve register contents + * Returns: + * 0 Success + * KDB_BADREG Invalid register name + * Locking: + * None. + * Remarks: + * If kdb was entered via an interrupt from the kernel itself then + * ss and esp are *not* on the stack. + */ + +static struct kdbregs { + char *reg_name; + size_t reg_offset; +} kdbreglist[] = { + { "eax", offsetof(struct pt_regs, eax) }, + { "ebx", offsetof(struct pt_regs, ebx) }, + { "ecx", offsetof(struct pt_regs, ecx) }, + { "edx", offsetof(struct pt_regs, edx) }, + + { "esi", offsetof(struct pt_regs, esi) }, + { "edi", offsetof(struct pt_regs, edi) }, + { "esp", offsetof(struct pt_regs, esp) }, + { "eip", offsetof(struct pt_regs, eip) }, + + { "ebp", offsetof(struct pt_regs, ebp) }, + { "xss", offsetof(struct pt_regs, xss) }, + { "xcs", offsetof(struct pt_regs, xcs) }, + { "eflags", offsetof(struct pt_regs, eflags) }, + + { "xds", offsetof(struct pt_regs, xds) }, + { "xes", offsetof(struct pt_regs, xes) }, + { "origeax", offsetof(struct pt_regs, orig_eax) }, + +}; + +static const int nkdbreglist = sizeof(kdbreglist) / sizeof(struct kdbregs); + +static struct kdbregs dbreglist[] = { + { "dr0", 0 }, + { "dr1", 1 }, + { "dr2", 2 }, + { "dr3", 3 }, + { "dr6", 6 }, + { "dr7", 7 }, +}; + +static const int ndbreglist = sizeof(dbreglist) / sizeof(struct kdbregs); + +int +kdba_getregcontents(const char *regname, + struct pt_regs *regs, + kdb_machreg_t *contents) +{ + int i; + + if (strcmp(regname, "®s") == 0) { + *contents = (unsigned long)regs; + return 0; + } + + if (strcmp(regname, "kesp") == 0) { + *contents = (unsigned long)regs + sizeof(struct pt_regs); + if ((regs->xcs & 0xffff) == __KERNEL_CS) { + /* esp and ss are not on stack */ + *contents -= 2*4; + } + return 0; + } + + if (strcmp(regname, "cesp") == 0) { + asm volatile("movl %%esp,%0":"=m" (*contents)); + return 0; + } + + if (strcmp(regname, "ceflags") == 0) { + int flags; + __save_flags(flags); + *contents = flags; + return 0; + } + + if (regname[0] == '%') { + /* User registers: %%e[a-c]x, etc */ + regname++; + regs = (struct pt_regs *) + (current->thread.esp0 - sizeof(struct pt_regs)); + } + + for (i=0; ixcs & 0xffff) == __KERNEL_CS) { + /* No cpl switch, esp and ss are not on stack */ + if (strcmp(kdbreglist[i].reg_name, "esp") == 0) { + *contents = (kdb_machreg_t)regs + + sizeof(struct pt_regs) - 2*4; + return(0); + } + if (strcmp(kdbreglist[i].reg_name, "xss") == 0) { + asm volatile( + "pushl %%ss\n" + "popl %0\n" + :"=m" (*contents)); + return(0); + } + } + *contents = *(unsigned long *)((unsigned long)regs + + kdbreglist[i].reg_offset); + return(0); + } + + for (i=0; i + * + * Parameters: + * regname Pointer to string naming register + * regs Pointer to structure containing registers. + * contents Unsigned long containing new register contents + * Outputs: + * Returns: + * 0 Success + * KDB_BADREG Invalid register name + * Locking: + * None. + * Remarks: + */ + +int +kdba_setregcontents(const char *regname, + struct pt_regs *regs, + unsigned long contents) +{ + int i; + + if (regname[0] == '%') { + regname++; + regs = (struct pt_regs *) + (current->thread.esp0 - sizeof(struct pt_regs)); + } + + for (i=0; ithread.esp0 - sizeof(struct pt_regs)); + } + + if (type == NULL) { + struct kdbregs *rlp; + kdb_machreg_t contents; + + for (i=0, rlp=kdbreglist; ieip; +} + +int +kdba_setpc(kdb_eframe_t ef, kdb_machreg_t newpc) +{ + ef->eip = newpc; + KDB_STATE_SET(IP_ADJUSTED); + return 0; +} + +/* + * kdba_main_loop + * + * Do any architecture specific set up before entering the main kdb loop. + * The primary function of this routine is to make all processes look the + * same to kdb, kdb must be able to list a process without worrying if the + * process is running or blocked, so make all process look as though they + * are blocked. + * + * Inputs: + * reason The reason KDB was invoked + * error The hardware-defined error code + * error2 kdb's current reason code. Initially error but can change + * acording to kdb state. + * db_result Result from break or debug point. + * ef The exception frame at time of fault/breakpoint. If reason + * is KDB_REASON_SILENT then ef is NULL, otherwise it should + * always be valid. + * Returns: + * 0 KDB was invoked for an event which it wasn't responsible + * 1 KDB handled the event for which it was invoked. + * Outputs: + * Sets eip and esp in current->thread. + * Locking: + * None. + * Remarks: + * none. + */ + +int +kdba_main_loop(kdb_reason_t reason, kdb_reason_t reason2, int error, + kdb_dbtrap_t db_result, kdb_eframe_t ef) +{ + if (ef) { + kdba_getregcontents("eip", ef, &(current->thread.eip)); + kdba_getregcontents("esp", ef, &(current->thread.esp)); + } + return(kdb_main_loop(reason, reason2, error, db_result, ef)); +} + +void +kdba_disableint(kdb_intstate_t *state) +{ + int *fp = (int *)state; + int flags; + + __save_flags(flags); + __cli(); + + *fp = flags; +} + +void +kdba_restoreint(kdb_intstate_t *state) +{ + int flags = *(int *)state; + __restore_flags(flags); +} + +void +kdba_setsinglestep(struct pt_regs *regs) +{ + if (regs->eflags & EF_IE) + KDB_STATE_SET(A_IF); + else + KDB_STATE_CLEAR(A_IF); + regs->eflags = (regs->eflags | EF_TF) & ~EF_IE; +} + +void +kdba_clearsinglestep(struct pt_regs *regs) +{ + if (KDB_STATE(A_IF)) + regs->eflags |= EF_IE; + else + regs->eflags &= ~EF_IE; +} + +int +kdb_getcurrentframe(struct pt_regs *regs) +{ + regs->xcs = 0; +#if defined(CONFIG_FRAME_POINTER) + asm volatile("movl %%ebp,%0":"=m" (*(int *)®s->ebp)); +#endif + asm volatile("movl %%esp,%0":"=m" (*(int *)®s->esp)); + + return 0; +} + +#ifdef KDB_HAVE_LONGJMP +int +kdb_setjmp(kdb_jmp_buf *jb) +{ +#if defined(CONFIG_FRAME_POINTER) + __asm__ ("movl 8(%esp), %eax\n\t" + "movl %ebx, 0(%eax)\n\t" + "movl %esi, 4(%eax)\n\t" + "movl %edi, 8(%eax)\n\t" + "movl (%esp), %ecx\n\t" + "movl %ecx, 12(%eax)\n\t" + "leal 8(%esp), %ecx\n\t" + "movl %ecx, 16(%eax)\n\t" + "movl 4(%esp), %ecx\n\t" + "movl %ecx, 20(%eax)\n\t"); +#else /* CONFIG_FRAME_POINTER */ + __asm__ ("movl 4(%esp), %eax\n\t" + "movl %ebx, 0(%eax)\n\t" + "movl %esi, 4(%eax)\n\t" + "movl %edi, 8(%eax)\n\t" + "movl %ebp, 12(%eax)\n\t" + "leal 4(%esp), %ecx\n\t" + "movl %ecx, 16(%eax)\n\t" + "movl 0(%esp), %ecx\n\t" + "movl %ecx, 20(%eax)\n\t"); +#endif /* CONFIG_FRAME_POINTER */ + KDB_STATE_SET(LONGJMP); + return 0; +} + +void +kdb_longjmp(kdb_jmp_buf *jb, int reason) +{ +#if defined(CONFIG_FRAME_POINTER) + __asm__("movl 8(%esp), %ecx\n\t" + "movl 12(%esp), %eax\n\t" + "movl 20(%ecx), %edx\n\t" + "movl 0(%ecx), %ebx\n\t" + "movl 4(%ecx), %esi\n\t" + "movl 8(%ecx), %edi\n\t" + "movl 12(%ecx), %ebp\n\t" + "movl 16(%ecx), %esp\n\t" + "jmp *%edx\n"); +#else /* CONFIG_FRAME_POINTER */ + __asm__("movl 4(%esp), %ecx\n\t" + "movl 8(%esp), %eax\n\t" + "movl 20(%ecx), %edx\n\t" + "movl 0(%ecx), %ebx\n\t" + "movl 4(%ecx), %esi\n\t" + "movl 8(%ecx), %edi\n\t" + "movl 12(%ecx), %ebp\n\t" + "movl 16(%ecx), %esp\n\t" + "jmp *%edx\n"); +#endif /* CONFIG_FRAME_POINTER */ +} +#endif /* KDB_HAVE_LONGJMP */ + + +/* + * kdba_enable_mce + * + * This function is called once on each CPU to enable machine + * check exception handling. + * + * Inputs: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + */ + +void +kdba_enable_mce(void) +{ + /* + * Enable Machine Check Exceptions + */ + if (test_bit(X86_FEATURE_MCE, &boot_cpu_data.x86_capability) && + test_bit(X86_FEATURE_MCA, &boot_cpu_data.x86_capability)) { + u32 i, lv, hv, count; + rdmsr(MCG_CAP, lv, hv); + count = lv&0xff; + if (lv & 0x100) { + hv = lv = 0xffffffff; + wrmsr(MCG_CTL, lv, hv); + } + for(i=1; i (unsigned long)high_memory) { + if (!kdb_vmlist_check(addr, addr+width)) { + /* + * Would appear to be an illegal kernel address; + * Print a message once, and don't print again until + * a legal address is used. + */ + if (!KDB_STATE(SUPPRESS)) { + kdb_printf("kdb: Bad kernel address 0x%lx\n", addr); + KDB_STATE_SET(SUPPRESS); + } + return 0L; + } + } + + /* + * A good address. Reset error flag. + */ + KDB_STATE_CLEAR(SUPPRESS); + + switch (width) { + case 4: + { unsigned long *lp; + + lp = (unsigned long *)(addr); + return *lp; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *)(addr); + return *sp; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *)(addr); + return *cp; + } + } + + kdb_printf("kdbgetword: Bad width\n"); + return 0L; +} + +/* + * kdba_putword + * + * Architecture specific function to access kernel virtual + * address space. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +unsigned long +kdba_putword(unsigned long addr, size_t size, unsigned long contents) +{ + /* + * This function checks the address for validity. Any address + * in the range PAGE_OFFSET to high_memory is legal, any address + * which maps to a vmalloc region is legal, and any address which + * is a user address, we use get_user() to verify validity. + */ + + if (addr < PAGE_OFFSET) { + /* + * Usermode address. + */ + unsigned long diag; + + switch (size) { + case 4: + { unsigned long *lp; + + lp = (unsigned long *) addr; + diag = put_user(contents, lp); + break; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *) addr; + diag = put_user(contents, sp); + break; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *) addr; + diag = put_user(contents, cp); + break; + } + default: + kdb_printf("kdba_putword: Bad width\n"); + return 0; + } + + if (diag) { + if (!KDB_STATE(SUPPRESS)) { + kdb_printf("kdb: Bad user address 0x%lx\n", addr); + KDB_STATE_SET(SUPPRESS); + } + return 0; + } + KDB_STATE_CLEAR(SUPPRESS); + return 0; + } + + if (addr > (unsigned long)high_memory) { + if (!kdb_vmlist_check(addr, addr+size)) { + /* + * Would appear to be an illegal kernel address; + * Print a message once, and don't print again until + * a legal address is used. + */ + if (!KDB_STATE(SUPPRESS)) { + kdb_printf("kdb: Bad kernel address 0x%lx\n", addr); + KDB_STATE_SET(SUPPRESS); + } + return 0L; + } + } + + /* + * A good address. Reset error flag. + */ + KDB_STATE_CLEAR(SUPPRESS); + + switch (size) { + case 4: + { unsigned long *lp; + + lp = (unsigned long *)(addr); + *lp = contents; + return 0; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *)(addr); + *sp = (unsigned short) contents; + return 0; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *)(addr); + *cp = (unsigned char) contents; + return 0; + } + } + + kdb_printf("kdba_putword: Bad width\n"); + return 0; +} + +/* + * kdba_callback_die + * + * Callback function for kernel 'die' function. + * + * Parameters: + * regs Register contents at time of trap + * error_code Trap-specific error code value + * trapno Trap number + * vp Pointer to die message + * Returns: + * Returns 1 if fault handled by kdb. + * Locking: + * None. + * Remarks: + * + */ +int +kdba_callback_die(struct pt_regs *regs, int error_code, long trapno, void *vp) +{ + /* + * Save a pointer to the message provided to 'die()'. + */ + kdb_diemsg = (char *)vp; + + return kdb(KDB_REASON_PANIC, error_code, (kdb_eframe_t) regs); +} + +/* + * kdba_callback_bp + * + * Callback function for kernel breakpoint trap. + * + * Parameters: + * regs Register contents at time of trap + * error_code Trap-specific error code value + * trapno Trap number + * vp Not Used. + * Returns: + * Returns 1 if fault handled by kdb. + * Locking: + * None. + * Remarks: + * + */ + +int +kdba_callback_bp(struct pt_regs *regs, int error_code, long trapno, void *vp) +{ + int diag; + + if (KDB_DEBUG(BP)) + kdb_printf("cb_bp: e_c = %d tn = %ld regs = 0x%p\n", error_code, + trapno, regs); + + diag = kdb(KDB_REASON_BREAK, error_code, (kdb_eframe_t) regs); + + if (KDB_DEBUG(BP)) + kdb_printf("cb_bp: e_c = %d tn = %ld regs = 0x%p diag = %d\n", error_code, + trapno, regs, diag); + return diag; +} + +/* + * kdba_callback_debug + * + * Callback function for kernel debug register trap. + * + * Parameters: + * regs Register contents at time of trap + * error_code Trap-specific error code value + * trapno Trap number + * vp Not used. + * Returns: + * Returns 1 if fault handled by kdb. + * Locking: + * None. + * Remarks: + * + */ + +int +kdba_callback_debug(struct pt_regs *regs, int error_code, long trapno, void *vp) +{ + return kdb(KDB_REASON_DEBUG, error_code, (kdb_eframe_t) regs); +} + +/* + * kdba_init + * + * Architecture specific initialization. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +void __init +kdba_init(void) +{ + kdba_enable_lbr(); + + return; +} + +/* + * kdba_adjust_ip + * + * Architecture specific adjustment of instruction pointer before leaving + * kdb. + * + * Parameters: + * reason The reason KDB was invoked + * error The hardware-defined error code + * ef The exception frame at time of fault/breakpoint. If reason + * is KDB_REASON_SILENT then ef is NULL, otherwise it should + * always be valid. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * noop on ix86. + */ + +void +kdba_adjust_ip(kdb_reason_t reason, int error, kdb_eframe_t ef) +{ + return; +} diff -urN 2.4.0/arch/i386/kernel/Makefile 2.4.0-ikd-1/arch/i386/kernel/Makefile --- 2.4.0/arch/i386/kernel/Makefile Tue Jan 2 17:41:12 2001 +++ 2.4.0-ikd-1/arch/i386/kernel/Makefile Mon Jan 15 18:40:04 2001 @@ -41,4 +41,10 @@ obj-$(CONFIG_X86_IO_APIC) += io_apic.o mpparse.o obj-$(CONFIG_X86_VISWS_APIC) += visws_apic.o +# Not safe to have tracing turned on in the init_task. That way lies deadlock. +ifeq ($(CONFIG_DEBUG_MCOUNT),y) +init_task.o: init_task.c $(TOPDIR)/include/linux/sched.h + $(CC) $(CFLAGS:%-pg=%-g -c) $(EXTRA_CFLAGS) -c -o $@ $< +endif + include $(TOPDIR)/Rules.make diff -urN 2.4.0/arch/i386/kernel/apic.c 2.4.0-ikd-1/arch/i386/kernel/apic.c --- 2.4.0/arch/i386/kernel/apic.c Thu Dec 14 22:33:59 2000 +++ 2.4.0-ikd-1/arch/i386/kernel/apic.c Mon Jan 15 18:40:04 2001 @@ -27,6 +27,8 @@ #include #include #include +#include +#include int prof_multiplier[NR_CPUS] = { 1, }; int prof_old_multiplier[NR_CPUS] = { 1, }; @@ -201,6 +203,62 @@ | APIC_DM_INIT); } +/* + * Set the counter value for the local APIC NMI watchdog. + */ + +int set_nmi_counter_local(void) +{ + extern unsigned long cpu_khz; + + if (!test_bit(X86_FEATURE_APIC, &boot_cpu_data.x86_capability)) + return(-EIO); + if (nmi_watchdog_source && nmi_watchdog_source != 1) + return(0); /* Not using local APIC */ + return wrmsr_eio(PERFCTR1, -(cpu_khz/HZ*1000), 0); +} + +/* + * Activate or deactivate NMI watchdog via a local APIC. + */ + +int setup_apic_nmi_watchdog(int value) +{ + int ret, eax; + + if (!test_bit(X86_FEATURE_APIC, &boot_cpu_data.x86_capability)) + return(-EIO); + if (nmi_watchdog_source && nmi_watchdog_source != 1) + return(0); /* Not using local APIC */ + /* Disable performance counters 0, 1 for all NMI changes */ + nmi_watchdog = proc_nmi_watchdog = nmi_watchdog_source = 0; + if ((ret = wrmsr_eio(EVNTSEL0, 0, 0)) || + (ret = wrmsr_eio(EVNTSEL1, 0, 0)) || + (ret = wrmsr_eio(PERFCTR0, 0, 0)) || + (ret = wrmsr_eio(PERFCTR1, 0, 0))) + goto exit; + if (!value) + return(0); + /* Must set before activation to catch first NMI */ + nmi_watchdog = proc_nmi_watchdog = nmi_watchdog_source = 1; + eax = 1 << 20 /* Interrupt on overflow */ + | 1 << 17 /* OS mode */ + | 1 << 16 /* User mode */ + | 0x79; /* Event, cpu clocks not halted */ + if ((ret = wrmsr_eio(EVNTSEL1, eax, 0)) + || (ret = set_nmi_counter_local())) + goto exit; + apic_write_around(APIC_LVTPC, APIC_DM_NMI); + eax = 1 << 22; /* Enable performance counters, only using ctr1 */ + ret = wrmsr_eio(EVNTSEL0, eax, 0); +exit: + if (ret) + nmi_watchdog = proc_nmi_watchdog = nmi_watchdog_source = 0; + else + printk(KERN_INFO "Activated NMI via local APIC\n"); + return(ret); +} + extern void __error_in_apic_c (void); void __init setup_local_APIC (void) @@ -338,18 +396,16 @@ { unsigned long apic_phys; - if (smp_found_config) { - apic_phys = mp_lapic_addr; - } else { - /* - * set up a fake all zeroes page to simulate the - * local APIC and another one for the IO-APIC. We - * could use the real zero-page, but it's safer - * this way if some buggy code writes to this page ... - */ - apic_phys = (unsigned long) alloc_bootmem_pages(PAGE_SIZE); - apic_phys = __pa(apic_phys); - } + /* + * Now that we make more of an effort to enable the local APIC even + * when we could find no SMP config information, we always map the + * APIC registers correctly (ie. the zero-page trick has been + * removed). See `IO_APIC_init_uniprocessor' in io_apic.c for more + * info. + */ + if ( !smp_found_config ) mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; + apic_phys = mp_lapic_addr; + set_fixmap_nocache(FIX_APIC_BASE, apic_phys); Dprintk("mapped APIC to %08lx (%08lx)\n", APIC_BASE, apic_phys); diff -urN 2.4.0/arch/i386/kernel/bluesmoke.c 2.4.0-ikd-1/arch/i386/kernel/bluesmoke.c --- 2.4.0/arch/i386/kernel/bluesmoke.c Tue Jan 2 17:41:12 2001 +++ 2.4.0-ikd-1/arch/i386/kernel/bluesmoke.c Mon Jan 15 18:40:04 2001 @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,7 @@ printk(KERN_EMERG "Attempting to continue.\n"); mcgstl&=~(1<<2); wrmsr(0x17a,mcgstl, mcgsth); + (void)kdb(KDB_REASON_NMI, error_code, regs); } diff -urN 2.4.0/arch/i386/kernel/entry.S 2.4.0-ikd-1/arch/i386/kernel/entry.S --- 2.4.0/arch/i386/kernel/entry.S Tue Nov 28 18:39:59 2000 +++ 2.4.0-ikd-1/arch/i386/kernel/entry.S Mon Jan 15 18:40:04 2001 @@ -185,6 +185,18 @@ jne tracesys_exit jmp ret_from_sys_call +#if defined(CONFIG_KDB) +ENTRY(kdb_call) + pushl %eax # save orig EAX + SAVE_ALL + pushl %esp # struct pt_regs + pushl $0 # error_code + pushl $7 # KDB_REASON_ENTRY + call SYMBOL_NAME(kdb) + addl $12,%esp # remove args + RESTORE_ALL +#endif + /* * Return to user mode is not as complex as all this looks, * but we want the default path for a system call return to @@ -196,6 +208,13 @@ pushl %eax # save orig_eax SAVE_ALL GET_CURRENT(%ebx) +#ifdef CONFIG_DEBUG_MCOUNT + pushl %eax + pushl %ebx + call SYMBOL_NAME(mcount) + popl %ebx + popl %eax +#endif cmpl $(NR_syscalls),%eax jae badsys testb $0x02,tsk_ptrace(%ebx) # PT_TRACESYS @@ -203,6 +222,11 @@ call *SYMBOL_NAME(sys_call_table)(,%eax,4) movl %eax,EAX(%esp) # save the return value ENTRY(ret_from_sys_call) +#ifdef CONFIG_DEBUG_MCOUNT + pushl %eax + call SYMBOL_NAME(mcount) + popl %eax +#endif #ifdef CONFIG_SMP movl processor(%ebx),%eax shll $CONFIG_X86_L1_CACHE_SHIFT,%eax @@ -228,16 +252,35 @@ testl $(VM_MASK),EFLAGS(%esp) movl %esp,%eax jne v86_signal_return +#ifndef CONFIG_KERNEL_DEBUGGING xorl %edx,%edx +#else + pushl $0 + pushl %eax +#endif call SYMBOL_NAME(do_signal) +#ifdef CONFIG_KERNEL_DEBUGGING + addl $8,%esp +#endif jmp restore_all ALIGN v86_signal_return: +#ifdef CONFIG_KERNEL_DEBUGGING + pushl %eax +#endif call SYMBOL_NAME(save_v86_state) movl %eax,%esp +#ifndef CONFIG_KERNEL_DEBUGGING xorl %edx,%edx +#else + pushl $0 + pushl %eax +#endif call SYMBOL_NAME(do_signal) +#ifdef CONFIG_KERNEL_DEBUGGING + addl $8,%esp +#endif jmp restore_all ALIGN @@ -406,6 +449,22 @@ ENTRY(alignment_check) pushl $ SYMBOL_NAME(do_alignment_check) jmp error_code + +#if defined(CONFIG_KDB) +ENTRY(page_fault_mca) + pushl %ecx + pushl %edx + pushl %eax + movl $473,%ecx + rdmsr + andl $0xfffffffe,%eax /* Disable last branch recording */ + wrmsr + popl %eax + popl %edx + popl %ecx + pushl $ SYMBOL_NAME(do_page_fault) + jmp error_code +#endif ENTRY(page_fault) pushl $ SYMBOL_NAME(do_page_fault) diff -urN 2.4.0/arch/i386/kernel/i8259.c 2.4.0-ikd-1/arch/i386/kernel/i8259.c --- 2.4.0/arch/i386/kernel/i8259.c Tue Nov 28 18:39:59 2000 +++ 2.4.0-ikd-1/arch/i386/kernel/i8259.c Mon Jan 15 18:40:04 2001 @@ -451,7 +451,11 @@ */ for (i = 0; i < NR_IRQS; i++) { int vector = FIRST_EXTERNAL_VECTOR + i; - if (vector != SYSCALL_VECTOR) + if ((vector != SYSCALL_VECTOR) +#if defined(CONFIG_KDB) + && (vector != KDBENTER_VECTOR) +#endif + ) set_intr_gate(vector, interrupt[i]); } diff -urN 2.4.0/arch/i386/kernel/io_apic.c 2.4.0-ikd-1/arch/i386/kernel/io_apic.c --- 2.4.0/arch/i386/kernel/io_apic.c Thu Nov 16 15:37:26 2000 +++ 2.4.0-ikd-1/arch/i386/kernel/io_apic.c Mon Jan 15 18:40:04 2001 @@ -1317,7 +1317,7 @@ apic_write_around(APIC_LVT0, v); } -static void setup_nmi (void) +static void setup_nmi_io (void) { /* * Dirty trick to enable the NMI watchdog ... @@ -1334,6 +1334,8 @@ enable_NMI_through_LVT0(NULL); printk(" done.\n"); + proc_nmi_watchdog = nmi_watchdog = 1; + nmi_watchdog_source = 2; } /* @@ -1435,7 +1437,7 @@ if (timer_irq_works()) { if (nmi_watchdog) { disable_8259A_irq(0); - setup_nmi(); + setup_nmi_io(); enable_8259A_irq(0); nmi_irq_works(); } @@ -1455,7 +1457,7 @@ if (timer_irq_works()) { printk("works.\n"); if (nmi_watchdog) { - setup_nmi(); + setup_nmi_io(); nmi_irq_works(); } return; @@ -1544,11 +1546,91 @@ */ void IO_APIC_init_uniprocessor (void) { + /* + * For real non-SMP machines, we now make more of an effort to + * actually enable the local APIC. We do this without enabling any IO + * APICs (without smp_config info we don't know if there are any!). + */ if (!smp_found_config) - return; + { + u32 h, l, dummy, features; + + if ( boot_cpu_data.x86 < 5 ) + { + printk("No local APIC on pre-Pentium Intel processors\n"); + return; + } + + if ( boot_cpu_data.x86 == 6 ) + { + /* + * Some BIOSes disable the local APIC in the APIC_BASE MSR. + * This can only be done in software for PPro and above. + */ + rdmsr(0x1b, l, h); + if ( !(l & 0x800) ) + { + printk("Local APIC disabled by BIOS -- reenabling...\n"); + l |= 0x800; + wrmsr(0x1b, l, h); + + /* The APIC feature bit should now be enabled in `cpuid' */ + cpuid(1, &dummy, &dummy, &dummy, &features); + if ( !(features & X86_FEATURE_APIC) ) + { + printk("Could not enable APIC -- support disabled\n"); + return; + } + } + } + + /* + * Make sure that we are processor 0, and that we are indicated + * as present. This stuff doesn't normally get done on a + * uniprocessor machine. + */ + phys_cpu_present_map = 1; + apic_read_around(APIC_ID); /* buggy P5 paranoia */ + apic_write_around(APIC_ID, 0); + + /* + * A final sanity check. Early P5 processors don't have a local + * APIC. Also, a P5 local APIC can be disabled in hardware! + */ + if ( apic_read(APIC_ID) != 0 ) + { + printk("Can't find local APIC: non-existent or disabled" + "in hardware\n"); + return; + } + + /* Now we know the APIC is enabled, update CPU features flags */ + set_bit(X86_FEATURE_APIC, &boot_cpu_data.x86_capability); + + /* + * Not sure about this, but it stops external INTs being masked! + * This is necessary because we don't initialise any IO APICs: + * (a) they weren't memory-mapped by `init_apic_mappings' because + * it didn't find any smp configuration info. + * (b) in a uniprocessor system, an IO APIC is redundant because + * all external INTs must go to the only processor. + */ + pic_mode = 1; + nr_ioapics = 0; + } + connect_bsp_APIC(); setup_local_APIC(); - setup_IO_APIC(); - setup_APIC_clocks(); + + /* + * We only set up IO APICs for a real SMP machine. Otherwise external + * INTs get masked locally, without them being routed to an IO APIC. + */ + if ( smp_found_config ) + { + setup_IO_APIC(); + } + + setup_APIC_clocks(); } #endif diff -urN 2.4.0/arch/i386/kernel/irq.c 2.4.0-ikd-1/arch/i386/kernel/irq.c --- 2.4.0/arch/i386/kernel/irq.c Thu Dec 14 22:33:59 2000 +++ 2.4.0-ikd-1/arch/i386/kernel/irq.c Mon Jan 15 18:40:04 2001 @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -305,6 +307,11 @@ static inline void get_irqlock(int cpu) { +#ifdef CONFIG_KDB + static int kdb_rate; + if (KDB_IS_RUNNING() && kdb_rate++ < 10) + kdb_printf("Warning: get_irqlock on cpu %d while kdb is running, may hang\n", smp_processor_id()); +#endif /* CONFIG_KDB */ if (test_and_set_bit(0,&global_irq_lock)) { /* do we already hold the lock? */ if ((unsigned char) cpu == global_irq_holder) @@ -624,6 +631,8 @@ if (softirq_active(cpu) & softirq_mask(cpu)) do_softirq(); + + MCOUNT(); return 1; } @@ -979,6 +988,8 @@ */ rand_initialize_irq(irq); } + + MCOUNT(); /* * The following block of code has to be executed atomically diff -urN 2.4.0/arch/i386/kernel/msr.c 2.4.0-ikd-1/arch/i386/kernel/msr.c --- 2.4.0/arch/i386/kernel/msr.c Tue Jan 2 17:41:12 2001 +++ 2.4.0-ikd-1/arch/i386/kernel/msr.c Mon Jan 15 18:40:04 2001 @@ -39,51 +39,7 @@ #include #include #include - -/* Note: "err" is handled in a funny way below. Otherwise one version - of gcc or another breaks. */ - -extern inline int wrmsr_eio(u32 reg, u32 eax, u32 edx) -{ - int err; - - asm volatile( - "1: wrmsr\n" - "2:\n" - ".section .fixup,\"ax\"\n" - "3: movl %4,%0\n" - " jmp 2b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 1b,3b\n" - ".previous" - : "=&bDS" (err) - : "a" (eax), "d" (edx), "c" (reg), "i" (-EIO), "0" (0)); - - return err; -} - -extern inline int rdmsr_eio(u32 reg, u32 *eax, u32 *edx) -{ - int err; - - asm volatile( - "1: rdmsr\n" - "2:\n" - ".section .fixup,\"ax\"\n" - "3: movl %4,%0\n" - " jmp 2b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 1b,3b\n" - ".previous" - : "=&bDS" (err), "=a" (*eax), "=d" (*edx) - : "c" (reg), "i" (-EIO), "0" (0)); - - return err; -} +#include #ifdef CONFIG_SMP diff -urN 2.4.0/arch/i386/kernel/process.c 2.4.0-ikd-1/arch/i386/kernel/process.c --- 2.4.0/arch/i386/kernel/process.c Fri Jan 5 02:19:26 2001 +++ 2.4.0-ikd-1/arch/i386/kernel/process.c Mon Jan 15 18:40:04 2001 @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -350,7 +351,16 @@ * Stop all CPUs and turn off local APICs and the IO-APIC, so * other OSs see a clean IRQ state. */ +#if defined(CONFIG_KDB) + /* + * If this restart is occuring while kdb is running (e.g. reboot + * command), the other CPU's are already stopped. Don't try to + * stop them yet again. + */ + if (!KDB_IS_RUNNING()) +#endif smp_send_stop(); + disable_IO_APIC(); #endif @@ -624,7 +634,7 @@ * More important, however, is the fact that this allows us much * more flexibility. */ -void __switch_to(struct task_struct *prev_p, struct task_struct *next_p) +void STDCALL(__switch_to(struct task_struct *prev_p, struct task_struct *next_p)) { struct thread_struct *prev = &prev_p->thread, *next = &next_p->thread; diff -urN 2.4.0/arch/i386/kernel/semaphore.c 2.4.0-ikd-1/arch/i386/kernel/semaphore.c --- 2.4.0/arch/i386/kernel/semaphore.c Tue Nov 28 18:39:59 2000 +++ 2.4.0-ikd-1/arch/i386/kernel/semaphore.c Mon Jan 15 18:40:04 2001 @@ -78,7 +78,12 @@ sem->sleepers = 1; /* us - see -1 above */ spin_unlock_irq(&semaphore_lock); +#ifdef CONFIG_SEMAPHORE_DEADLOCK + if (!schedule_timeout(20*HZ)) + BUG(); +#else schedule(); +#endif tsk->state = TASK_UNINTERRUPTIBLE; spin_lock_irq(&semaphore_lock); } @@ -128,7 +133,12 @@ sem->sleepers = 1; /* us - see -1 above */ spin_unlock_irq(&semaphore_lock); +#ifdef CONFIG_SEMAPHORE_DEADLOCK + if (!schedule_timeout(20*HZ)) + BUG(); +#else schedule(); +#endif tsk->state = TASK_INTERRUPTIBLE; spin_lock_irq(&semaphore_lock); } @@ -182,6 +192,10 @@ ".align 4\n" ".globl __down_failed\n" "__down_failed:\n\t" +#if defined(CONFIG_KERNEL_DEBUGGING) + "pushl %ebp\n\t" + "movl %esp,%ebp\n\t" +#endif "pushl %eax\n\t" "pushl %edx\n\t" "pushl %ecx\n\t" @@ -189,6 +203,10 @@ "popl %ecx\n\t" "popl %edx\n\t" "popl %eax\n\t" +#if defined(CONFIG_KERNEL_DEBUGGING) + "movl %ebp,%esp\n\t" + "popl %ebp\n\t" +#endif "ret" ); @@ -196,11 +214,19 @@ ".align 4\n" ".globl __down_failed_interruptible\n" "__down_failed_interruptible:\n\t" +#if defined(CONFIG_KERNEL_DEBUGGING) + "pushl %ebp\n\t" + "movl %esp,%ebp\n\t" +#endif "pushl %edx\n\t" "pushl %ecx\n\t" "call __down_interruptible\n\t" "popl %ecx\n\t" "popl %edx\n\t" +#if defined(CONFIG_KERNEL_DEBUGGING) + "movl %ebp,%esp\n\t" + "popl %ebp\n\t" +#endif "ret" ); @@ -208,11 +234,19 @@ ".align 4\n" ".globl __down_failed_trylock\n" "__down_failed_trylock:\n\t" +#if defined(CONFIG_KERNEL_DEBUGGING) + "pushl %ebp\n\t" + "movl %esp,%ebp\n\t" +#endif "pushl %edx\n\t" "pushl %ecx\n\t" "call __down_trylock\n\t" "popl %ecx\n\t" "popl %edx\n\t" +#if defined(CONFIG_KERNEL_DEBUGGING) + "movl %ebp,%esp\n\t" + "popl %ebp\n\t" +#endif "ret" ); @@ -296,7 +330,12 @@ break; set_task_state(tsk, TASK_UNINTERRUPTIBLE); if (!sem->read_bias_granted) +#ifdef CONFIG_SEMAPHORE_DEADLOCK + if (!schedule_timeout(20*HZ)) + BUG(); +#else schedule(); +#endif } remove_wait_queue(&sem->wait, &wait); @@ -317,7 +356,12 @@ break; set_task_state(tsk, TASK_UNINTERRUPTIBLE); if (!sem->write_bias_granted) +#ifdef CONFIG_SEMAPHORE_DEADLOCK + if (!schedule_timeout(20*HZ)) + BUG(); +#else schedule(); +#endif } remove_wait_queue(&sem->write_bias_wait, &wait); @@ -349,7 +393,12 @@ set_task_state(tsk, TASK_UNINTERRUPTIBLE); if (atomic_read(&sem->count) >= 0) break; +#ifdef CONFIG_SEMAPHORE_DEADLOCK + if (!schedule_timeout(20*HZ)) + BUG(); +#else schedule(); +#endif } remove_wait_queue(&sem->wait, &wait); @@ -374,7 +423,12 @@ set_task_state(tsk, TASK_UNINTERRUPTIBLE); if (atomic_read(&sem->count) >= 0) break; /* we must attempt to acquire or bias the lock */ +#ifdef CONFIG_SEMAPHORE_DEADLOCK + if (!schedule_timeout(20*HZ)) + BUG(); +#else schedule(); +#endif } remove_wait_queue(&sem->wait, &wait); diff -urN 2.4.0/arch/i386/kernel/smp.c 2.4.0-ikd-1/arch/i386/kernel/smp.c --- 2.4.0/arch/i386/kernel/smp.c Tue Jan 2 17:41:12 2001 +++ 2.4.0-ikd-1/arch/i386/kernel/smp.c Mon Jan 15 18:40:04 2001 @@ -21,6 +21,8 @@ #include #include +#include + /* * Some notes on x86 processor bugs affecting SMP operation: * @@ -142,6 +144,15 @@ */ cfg = __prepare_ICR(shortcut, vector); +#if defined(CONFIG_KDB) + if (vector == KDB_VECTOR) { + /* + * Setup KDB IPI to be delivered as an NMI + */ + cfg = (cfg&~APIC_VECTOR_MASK)|APIC_DM_NMI; + } +#endif /* CONFIG_KDB */ + /* * Send the IPI. The write to APIC_ICR fires this off. */ @@ -405,6 +416,14 @@ do_flush_tlb_all_local(); } + +#if defined(CONFIG_KDB) +void +smp_kdb_stop(void) +{ + send_IPI_allbutself(KDB_VECTOR); +} +#endif /* CONFIG_KDB */ /* * this function sends a 'reschedule' IPI to another CPU. diff -urN 2.4.0/arch/i386/kernel/smpboot.c 2.4.0-ikd-1/arch/i386/kernel/smpboot.c --- 2.4.0/arch/i386/kernel/smpboot.c Tue Jan 2 17:41:12 2001 +++ 2.4.0-ikd-1/arch/i386/kernel/smpboot.c Mon Jan 15 18:40:04 2001 @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -426,6 +427,11 @@ */ smp_store_cpu_info(cpuid); +#ifdef CONFIG_KDB + /* Activate any preset global breakpoints on this cpu */ + kdb(KDB_REASON_SILENT, 0, 0); +#endif /* CONFIG_KDB */ + /* * Allow the master to continue. */ @@ -544,6 +550,8 @@ unsigned long send_status, accept_status, boot_status, maxlvt; int timeout, num_starts, j, cpu; unsigned long start_eip; + + nmi_watchdog_source = 2; /* Must assume that we have a working IO-APIC */ cpu = ++cpucount; /* diff -urN 2.4.0/arch/i386/kernel/time.c 2.4.0-ikd-1/arch/i386/kernel/time.c --- 2.4.0/arch/i386/kernel/time.c Tue Jan 2 17:41:12 2001 +++ 2.4.0-ikd-1/arch/i386/kernel/time.c Mon Jan 15 18:40:04 2001 @@ -410,8 +410,10 @@ * system, in that case we have to call the local interrupt handler. */ #ifndef CONFIG_X86_LOCAL_APIC +#ifndef CONFIG_PROFILE_GCC if (!user_mode(regs)) x86_do_profile(regs->eip); +#endif #else if (!smp_found_config) smp_local_timer_interrupt(regs); diff -urN 2.4.0/arch/i386/kernel/traps.c 2.4.0-ikd-1/arch/i386/kernel/traps.c --- 2.4.0/arch/i386/kernel/traps.c Fri Jan 5 02:19:26 2001 +++ 2.4.0-ikd-1/arch/i386/kernel/traps.c Mon Jan 15 18:40:04 2001 @@ -22,13 +22,18 @@ #include #include #include +#include #include +#include /* mcount debugger */ #ifdef CONFIG_MCA #include #include #endif +#include +#include + #include #include #include @@ -49,6 +54,9 @@ #include asmlinkage int system_call(void); +#if defined(CONFIG_KDB) +asmlinkage int kdb_call(void); +#endif asmlinkage void lcall7(void); asmlinkage void lcall27(void); @@ -79,6 +87,9 @@ asmlinkage void stack_segment(void); asmlinkage void general_protection(void); asmlinkage void page_fault(void); +#if defined(CONFIG_KDB) +asmlinkage void page_fault_mca(void); +#endif asmlinkage void coprocessor_error(void); asmlinkage void simd_coprocessor_error(void); asmlinkage void alignment_check(void); @@ -92,6 +103,53 @@ * segments. */ +#ifdef CONFIG_KERNEL_DEBUGGING +inline void print_call_trace_exact (struct pt_regs * regs, unsigned long esp) +{ + int i=1; + unsigned long *this_stack, *prev_stack, prev_addr, *prev_bp, framesize; + + printk("\nCall Trace: "); + + /* + * the stack layout: /----- *this_stack + * V + * [this_frame][prev_bp][prev_addr][prev_frame][...] + */ + + this_stack = (unsigned long *) regs->ebp; + framesize=0; + + while ((unsigned long) this_stack >= (esp & ~0x1fffUL) && + (unsigned long) (this_stack+1) < + (esp & ~0x1fffUL)+0x2000UL) + { + prev_addr = *(this_stack+1); + + if (!(i++ % 8)) + printk("\n "); + /* ksymoops expects [] */ + printk("[<%08lx>] (%lu) ", prev_addr, framesize); + + prev_bp = (unsigned long *)(*this_stack); + prev_stack = this_stack; + this_stack = prev_bp; + + if (i > 100) + { + printk("WARNING: something fishy with the stack frame?\n"); + printk("this_stack: [<%08lx>]\n", + (unsigned long)this_stack); + break; + } + framesize = (unsigned long)this_stack-(unsigned long)prev_stack; + } +#ifdef CONFIG_TRACE + print_emergency_trace(); +#endif +} +#endif /* CONFIG_KERNEL_DEBUGGING */ + void show_trace(unsigned long * stack) { int i; @@ -100,6 +158,7 @@ if (!stack) stack = (unsigned long*)&stack; +#ifndef CONFIG_KERNEL_DEBUGGING printk("Call Trace: "); i = 1; module_start = VMALLOC_START; @@ -123,30 +182,7 @@ i++; } } - printk("\n"); -} - -void show_stack(unsigned long * esp) -{ - unsigned long *stack; - int i; - - // debugging aid: "show_stack(NULL);" prints the - // back trace for this cpu. - - if(esp==NULL) - esp=(unsigned long*)&esp; - - stack = esp; - for(i=0; i < kstack_depth_to_print; i++) { - if (((long) stack & (THREAD_SIZE-1)) == 0) - break; - if (i && ((i % 8) == 0)) - printk("\n "); - printk("%08lx ", *stack++); - } - printk("\n"); - show_trace(esp); +#endif /*CONFIG_KERNEL_DEBUGGING*/ } static void show_registers(struct pt_regs *regs) @@ -181,6 +217,12 @@ printk("\nStack: "); show_stack((unsigned long*)esp); +#ifdef CONFIG_KERNEL_DEBUGGING + /* + * If debugging is switched on then we can walk the stack frame. + */ + print_call_trace_exact(regs, esp); +#endif printk("\nCode: "); if(regs->eip < PAGE_OFFSET) @@ -200,17 +242,176 @@ printk("\n"); } +#if defined(CONFIG_KDB) +spinlock_t dblist_lock = SPIN_LOCK_UNLOCKED; + +#define MAXDBLIST 8 + +typedef int (*dbfunc_t)(struct pt_regs * regs, int error_code, + long trap, void *value); + +typedef struct __db_list_s { + struct __db_list_s *db_next; + dbfunc_t db_func; +} dblist_t; + +typedef struct __db_listhead_s { + dblist_t dblh_list[MAXDBLIST]; + dblist_t *dblh_head; + char *dblh_name; +} dblisthead_t; + + /* + * Hook-up list to 'die' function + */ +static dblisthead_t dblist_die = + { {{ NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }}, + NULL, + "die" + }; + + /* + * Hook-up list to int3 + */ +static dblisthead_t dblist_int3 = + { {{ NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }}, + NULL, + "int3" + }; + + /* + * Hook-up list to debug trap + */ +static dblisthead_t dblist_debug = + { {{ NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }}, + NULL, + "debug" + }; + +static void register_dbfunc(dblisthead_t *lhp, dbfunc_t dfp) +{ + int i; + + spin_lock(&dblist_lock); + + if (KDB_DEBUG(CALLBACK)) + kdb_printf("listhead 0x%p func 0x%p\n", lhp, dfp); + + for(i=0; idblh_list[i].db_func == NULL) { + break; + } + } + if (i == MAXDBLIST) { + if (KDB_DEBUG(CALLBACK)) + kdb_printf("register_dbfunc: 0x%p not registered for %s\n", + dfp, lhp->dblh_name); + spin_unlock(&dblist_lock); + return; + } + + lhp->dblh_list[i].db_func = dfp; + lhp->dblh_list[i].db_next = lhp->dblh_head; + lhp->dblh_head = &lhp->dblh_list[i]; + + spin_unlock(&dblist_lock); +} + +void register_die(dbfunc_t dfp) +{ + register_dbfunc(&dblist_die, dfp); +} + +void register_int3(dbfunc_t dfp) +{ + register_dbfunc(&dblist_int3, dfp); +} + +void register_debug(dbfunc_t dfp) +{ + register_dbfunc(&dblist_debug, dfp); +} + +static inline int +callout_dbfunc(dblisthead_t *lhp, struct pt_regs *regs, int error_code, + long trap_number, void *parameter) +{ + dblist_t *dlp = lhp->dblh_head; + int diag = 0; + + if (KDB_DEBUG(CALLBACK)) + kdb_printf("callout dbfunc: cpu %d lhp 0x%p\n", smp_processor_id(), lhp); + /* If we oops inside kdb, we already have the dblist_lock */ + if (!KDB_IS_RUNNING()) + spin_lock(&dblist_lock); + while (dlp) { + int rv; + + /* + * The first callout function to handle this callout + * condition will return '1'. No other callout handlers + * will be invoked. Errors inside kdb will longjmp + * instead of returning. + */ + if (KDB_DEBUG(CALLBACK)) + kdb_printf("callout dbfunc: cpu %d func 0x%p\n", smp_processor_id(), dlp->db_func); + rv = dlp->db_func(regs, error_code, trap_number, parameter); + if (KDB_DEBUG(CALLBACK)) + kdb_printf("callout cpu %d diag %d\n", smp_processor_id(), rv); + if (rv) { + diag ++; + break; + } + + dlp = dlp->db_next; + } + if (!KDB_IS_RUNNING()) + spin_unlock(&dblist_lock); + if (KDB_DEBUG(CALLBACK)) + kdb_printf("callout dbfunc: cpu %d end\n", smp_processor_id()); + + return diag; +} +#endif + spinlock_t die_lock = SPIN_LOCK_UNLOCKED; void die(const char * str, struct pt_regs * regs, long err) { + unsigned long flags; + SUSPEND_MCOUNT; console_verbose(); - spin_lock_irq(&die_lock); +#if defined(CONFIG_KDB) + (void) callout_dbfunc(&dblist_die, regs, err, -1, (void *)str); +#endif + spin_lock_irqsave(&die_lock, flags); printk("%s: %04lx\n", str, err & 0xffff); show_registers(regs); - spin_unlock_irq(&die_lock); + spin_unlock_irqrestore(&die_lock, flags); do_exit(SIGSEGV); + RESUME_MCOUNT; } static inline void die_if_kernel(const char * str, struct pt_regs * regs, long err) @@ -261,6 +462,30 @@ if (ret) goto trap_signal; return; } + printk("\n"); +} + +void show_stack(unsigned long * esp) +{ + unsigned long *stack; + int i; + + // debugging aid: "show_stack(NULL);" prints the + // back trace for this cpu. + + if(esp==NULL) + esp=(unsigned long*)&esp; + + stack = esp; + for(i=0; i < kstack_depth_to_print; i++) { + if (((long) stack & (THREAD_SIZE-1)) == 0) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%08lx ", *stack++); + } + printk("\n"); + show_trace(esp); } #define DO_ERROR(trapnr, signr, str, name) \ @@ -298,7 +523,9 @@ } DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->eip) +#if !defined(CONFIG_KDB) DO_VM86_ERROR( 3, SIGTRAP, "int3", int3) +#endif DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow) DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds) DO_ERROR_INFO( 6, SIGILL, "invalid operand", invalid_op, ILL_ILLOPN, regs->eip) @@ -375,6 +602,7 @@ return; } #endif + (void)kdb(KDB_REASON_NMI, reason, regs); printk("Uhhuh. NMI received for unknown reason %02x.\n", reason); printk("Dazed and confused, but trying to continue\n"); printk("Do you have a strange power saving mode enabled?\n"); @@ -382,22 +610,66 @@ #if CONFIG_X86_IO_APIC +/* We can't run the nmi watchdog with config-trace or config-stack-meter + because mcount() must grab spinlocks with such two options enabled + and the __cli() won't be enough to protect us from a nmi (that will + wants to run mcount too so recursing on the spinlock). */ +#if (defined(CONFIG_SMP) || defined(CONFIG_UP_NMI_WATCHDOG)) \ + && !(defined(CONFIG_TRACE) || defined(CONFIG_KSTACK_METER)) int nmi_watchdog = 1; +#else +int nmi_watchdog; +#endif +int proc_nmi_watchdog; /* Visible/changeable via /proc */ +int nmi_watchdog_source; /* 1 local APIC, 2 IO-APIC */ + +#if !defined(CONFIG_TRACE) && !defined(CONFIG_KSTACK_METER) +int set_nmi_watchdog(int value) +{ + int ret = -ENOSYS; + if (value != nmi_watchdog && nmi_watchdog_source == 2) { + /* FIXME: No code to disable/enable IO-APIC NMI watchdog yet */ + printk(KERN_WARNING "NMI watchdog using IO-APIC, cannot change after boot\n"); + proc_nmi_watchdog = nmi_watchdog; + } + else if ((ret = setup_apic_nmi_watchdog(value))) { + printk(KERN_WARNING "set_nmi_watchdog failed %d, value not changed\n", ret); + proc_nmi_watchdog = nmi_watchdog; + } + return ret; +} static int __init setup_nmi_watchdog(char *str) { - get_option(&str, &nmi_watchdog); - return 1; + get_option(&str, &nmi_watchdog); + if (nmi_watchdog) + nmi_watchdog = 1; + return 1; } __setup("nmi_watchdog=", setup_nmi_watchdog); +#else +int set_nmi_watchdog(int value) +{ + printk(KERN_ERR "Sorry, ktrace or stackmeter active.\n"); + return -EPERM; +} +#endif static spinlock_t nmi_print_lock = SPIN_LOCK_UNLOCKED; +#if defined(CONFIG_SMP) && defined(CONFIG_KDB) +static void +do_ack_apic_irq(void) +{ + ack_APIC_irq(); +} +#endif + inline void nmi_watchdog_tick(struct pt_regs * regs) { /* - * the best way to detect wether a CPU has a 'hard lockup' problem + * the best way to detect whether a CPU has a 'hard lockup' problem * is to check it's local APIC timer IRQ counts. If they are not * changing then that CPU has some problem. * @@ -419,6 +691,12 @@ * the stack NMI-atomically, it's safe to use smp_processor_id(). */ int sum, cpu = smp_processor_id(); + set_nmi_counter_local(); /* May be using local APIC */ +#if defined(CONFIG_KDB) + /* Ignore watchdog if kdb says so */ + if (KDB_STATE(NO_WATCHDOG)) + return; +#endif /* defined(CONFIG_KDB) */ sum = apic_timer_irqs[cpu]; @@ -437,6 +715,14 @@ bust_spinlocks(); printk("NMI Watchdog detected LOCKUP on CPU%d, registers:\n", cpu); show_registers(regs); +#if defined(CONFIG_KDB) + if (kdb_on) { + /* Invoke kdb and try to continue in the same state afterwards */ + spin_unlock(&nmi_print_lock); + kdb(KDB_REASON_WATCHDOG, 0, regs); + spin_lock(&nmi_print_lock); + } +#endif /* CONFIG_KDB */ printk("console shuts up ...\n"); console_silent(); spin_unlock(&nmi_print_lock); @@ -447,14 +733,23 @@ alert_counter[cpu] = 0; } } -#endif +#endif /* CONFIG_X86_IO_APIC */ asmlinkage void do_nmi(struct pt_regs * regs, long error_code) { unsigned char reason = inb(0x61); - ++nmi_count(smp_processor_id()); + +#if defined(CONFIG_SMP) && defined(CONFIG_KDB) + /* + * Call the kernel debugger to see if this NMI is due + * to an KDB requested IPI. If so, kdb will handle it. + */ + if (kdb_ipi((kdb_eframe_t)regs, do_ack_apic_irq)) { + return; + } +#endif if (!(reason & 0xc0)) { #if CONFIG_X86_IO_APIC /* @@ -466,9 +761,9 @@ return; } else unknown_nmi_error(reason, regs); -#else +#else /* ! CONFIG_X86_IO_APIC */ unknown_nmi_error(reason, regs); -#endif +#endif /* ! CONFIG_X86_IO_APIC */ return; } if (reason & 0x80) @@ -515,6 +810,15 @@ __asm__ __volatile__("movl %%db6,%0" : "=r" (condition)); +#if defined(CONFIG_KDB) + /* + * The callout functions will return 'true' if they've handled + * the callout condition. + */ + if (callout_dbfunc(&dblist_debug, regs, error_code, SIGTRAP, NULL)) + return; +#endif + /* Mask out spurious debug traps due to lazy DR7 setting */ if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { if (!tsk->thread.debugreg[7]) @@ -574,6 +878,16 @@ return; } +#if defined(CONFIG_KDB) +asmlinkage void do_int3(struct pt_regs * regs, long error_code) +{ + if (callout_dbfunc(&dblist_int3, regs, error_code, SIGTRAP, NULL)) + return; + + do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL); +} +#endif + /* * Note that we play around with the 'TS' bit in an attempt to get * the correct behaviour even in the presence of the asynchronous @@ -967,7 +1281,17 @@ set_trap_gate(11,&segment_not_present); set_trap_gate(12,&stack_segment); set_trap_gate(13,&general_protection); +#if defined(CONFIG_KDB) + if (test_bit(X86_FEATURE_MCE, &boot_cpu_data.x86_capability) && + test_bit(X86_FEATURE_MCA, &boot_cpu_data.x86_capability)) { + set_trap_gate(14,&page_fault_mca); + } + else { + set_trap_gate(14,&page_fault); + } +#else set_trap_gate(14,&page_fault); +#endif set_trap_gate(15,&spurious_interrupt_bug); set_trap_gate(16,&coprocessor_error); set_trap_gate(17,&alignment_check); @@ -975,6 +1299,17 @@ set_trap_gate(19,&simd_coprocessor_error); set_system_gate(SYSCALL_VECTOR,&system_call); +#if defined(CONFIG_KDB) + { + set_trap_gate(18, &machine_check); + } + kdb_enablehwfault(); + /* + * A trap gate, used by the kernel to enter the + * debugger, preserving all registers. + */ + set_trap_gate(KDBENTER_VECTOR, &kdb_call); +#endif /* CONFIG_KDB */ /* * default LDT is a single-entry callgate to lcall7 for iBCS @@ -992,5 +1327,11 @@ superio_init(); lithium_init(); cobalt_init(); +#endif + +#if defined(CONFIG_KDB) + register_die(kdba_callback_die); + register_int3(kdba_callback_bp); + register_debug(kdba_callback_debug); #endif } diff -urN 2.4.0/arch/i386/mm/fault.c 2.4.0-ikd-1/arch/i386/mm/fault.c --- 2.4.0/arch/i386/mm/fault.c Tue Nov 28 18:39:59 2000 +++ 2.4.0-ikd-1/arch/i386/mm/fault.c Mon Jan 15 18:40:04 2001 @@ -4,6 +4,7 @@ * Copyright (C) 1995 Linus Torvalds */ +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include #include @@ -266,6 +268,8 @@ bust_spinlocks(); + /* recursion is the curse of the programming classes */ + SUSPEND_MCOUNT_PROC(current); if (address < PAGE_SIZE) printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); else diff -urN 2.4.0/arch/i386/mm/init.c 2.4.0-ikd-1/arch/i386/mm/init.c --- 2.4.0/arch/i386/mm/init.c Thu Dec 14 22:33:59 2000 +++ 2.4.0-ikd-1/arch/i386/mm/init.c Mon Jan 15 18:40:04 2001 @@ -84,7 +84,11 @@ } #endif +#ifndef CONFIG_MEMLEAK +pte_t * get_bad_pte_table(void) +#else static pte_t * get_bad_pte_table(void) +#endif { pte_t v; int i; @@ -111,6 +115,7 @@ set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(get_bad_pte_table()))); } +#ifndef CONFIG_MEMLEAK /* put these back into pgtable.h */ pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) { pte_t *pte; @@ -154,6 +159,7 @@ } return (pte_t *) pmd_page(*pmd) + offset; } +#endif CONFIG_MEMLEAK int do_check_pgt_cache(int low, int high) { diff -urN 2.4.0/arch/i386/vmlinux.lds 2.4.0-ikd-1/arch/i386/vmlinux.lds --- 2.4.0/arch/i386/vmlinux.lds Fri Jan 5 02:19:26 2001 +++ 2.4.0-ikd-1/arch/i386/vmlinux.lds Mon Jan 15 18:40:04 2001 @@ -29,6 +29,10 @@ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + .data : { /* Data */ *(.data) CONSTRUCTORS diff -urN 2.4.0/arch/ia64/config.in 2.4.0-ikd-1/arch/ia64/config.in --- 2.4.0/arch/ia64/config.in Fri Jan 5 02:19:26 2001 +++ 2.4.0-ikd-1/arch/ia64/config.in Mon Jan 15 18:40:04 2001 @@ -256,4 +256,10 @@ bool 'Enable new unwind support' CONFIG_IA64_NEW_UNWIND bool 'Disable VHPT' CONFIG_DISABLE_VHPT +source kernel/debug/Config.in +# arch specific debugging options +if [ "$CONFIG_KERNEL_DEBUGGING" = "y" ]; then + bool ' Print %eip to resolve symbols from locks' CONFIG_PRINT_EIP n +fi + endmenu diff -urN 2.4.0/arch/ia64/kernel/Makefile 2.4.0-ikd-1/arch/ia64/kernel/Makefile --- 2.4.0/arch/ia64/kernel/Makefile Fri Jan 5 02:19:26 2001 +++ 2.4.0-ikd-1/arch/ia64/kernel/Makefile Mon Jan 15 18:40:04 2001 @@ -9,6 +9,7 @@ all: kernel.o head.o init_task.o + O_TARGET := kernel.o obj-y := acpi.o entry.o gate.o efi.o efi_stub.o irq.o irq_ia64.o irq_sapic.o ivt.o \ @@ -21,9 +22,16 @@ obj-$(CONFIG_SMP) += smp.o smpboot.o obj-$(CONFIG_IA64_MCA) += mca.o mca_asm.o obj-$(CONFIG_IA64_BRL_EMU) += brl_emu.o +obj-$(CONFIG_DEBUG_MCOUNT) += _mcount.o export-objs := ia64_ksyms.o clean:: + +# Not safe to have tracing turned on in the init_task. That way lies deadlock. +ifeq ($(CONFIG_KERNEL_DEBUGGING),y) +init_task.o: init_task.c $(TOPDIR)/include/linux/sched.h + $(CC) $(CFLAGS:%-pg=%-g -c) $(EXTRA_CFLAGS) -c -o $@ $< +endif include $(TOPDIR)/Rules.make diff -urN 2.4.0/arch/ia64/kernel/_mcount.S 2.4.0-ikd-1/arch/ia64/kernel/_mcount.S --- 2.4.0/arch/ia64/kernel/_mcount.S Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/arch/ia64/kernel/_mcount.S Mon Jan 15 18:40:04 2001 @@ -0,0 +1,37 @@ + .text + .psr abi64 + .psr lsb + .lsb + + .align 16 + .global _mcount + .proc _mcount +_mcount: + alloc loc0 = ar.pfs, 4, 3, 3, 0 + mov loc1 = rp + mov loc2 = r8 // gcc uses r8 to pass pointer to return structure + ;; + mov out0 = in2 + mov out1 = rp + br.call.sptk.few rp = mcount + ;; +.here: +{ + .mii + mov gp = in1 + mov r2 = ip + mov ar.pfs = loc0 +} + ;; + adds r2 = 1f - .here, r2 + mov b7 = loc1 + mov rp = in2 + ;; + mov r8 = loc2 + mov b6 = r2 + br.ret.sptk.few b6 + +1: alloc r2 = ar.pfs, 0, 0, 9, 0 + mov ar.pfs = r40 + br b7 + .endp _mcount diff -urN 2.4.0/arch/m68k/vmlinux-sun3.lds 2.4.0-ikd-1/arch/m68k/vmlinux-sun3.lds --- 2.4.0/arch/m68k/vmlinux-sun3.lds Sat Sep 4 22:06:41 1999 +++ 2.4.0-ikd-1/arch/m68k/vmlinux-sun3.lds Mon Jan 15 18:40:04 2001 @@ -29,6 +29,9 @@ __start___ksymtab = .; /* Kernel symbol table */ *(__ksymtab) __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + *(__kallsyms) + __stop___kallsyms = .; } /* End of data goes *here* so that freeing init code works properly. */ _edata = .; diff -urN 2.4.0/arch/m68k/vmlinux.lds 2.4.0-ikd-1/arch/m68k/vmlinux.lds --- 2.4.0/arch/m68k/vmlinux.lds Sat Sep 4 22:06:41 1999 +++ 2.4.0-ikd-1/arch/m68k/vmlinux.lds Mon Jan 15 18:40:04 2001 @@ -24,6 +24,10 @@ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + _etext = .; /* End of text section */ .data : { /* Data */ diff -urN 2.4.0/arch/ppc/vmlinux.lds 2.4.0-ikd-1/arch/ppc/vmlinux.lds --- 2.4.0/arch/ppc/vmlinux.lds Fri Aug 4 20:23:37 2000 +++ 2.4.0-ikd-1/arch/ppc/vmlinux.lds Mon Jan 15 18:40:04 2001 @@ -69,6 +69,10 @@ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + . = ALIGN(32); .data.cacheline_aligned : { *(.data.cacheline_aligned) } diff -urN 2.4.0/arch/sparc64/kernel/time.c 2.4.0-ikd-1/arch/sparc64/kernel/time.c --- 2.4.0/arch/sparc64/kernel/time.c Thu Nov 16 15:37:27 2000 +++ 2.4.0-ikd-1/arch/sparc64/kernel/time.c Mon Jan 15 18:40:04 2001 @@ -70,6 +70,7 @@ void sparc64_do_profile(unsigned long pc, unsigned long o7) { +#ifndef CONFIG_PROFILE_GCC if (prof_buffer && current->pid) { extern int _stext; extern int rwlock_impl_begin, rwlock_impl_end; @@ -94,6 +95,7 @@ pc = prof_len - 1; atomic_inc((atomic_t *)&prof_buffer[pc]); } +#endif } static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) diff -urN 2.4.0/drivers/char/Makefile 2.4.0-ikd-1/drivers/char/Makefile --- 2.4.0/drivers/char/Makefile Fri Jan 5 02:19:27 2001 +++ 2.4.0-ikd-1/drivers/char/Makefile Mon Jan 15 18:40:04 2001 @@ -25,7 +25,7 @@ misc.o pty.o random.o selection.o serial.o \ tty_io.o -mod-subdirs := joystick ftape drm pcmcia +mod-subdirs := joystick ftape drm pcmcia list-multi := @@ -200,4 +200,4 @@ consolemap_deftbl.o: consolemap_deftbl.c $(TOPDIR)/include/linux/types.h defkeymap.c: defkeymap.map - loadkeys --mktable defkeymap.map > defkeymap.c + loadkeys --mktable defkeymap.map | sed -e 's/^static *//' > defkeymap.c diff -urN 2.4.0/drivers/char/keyboard.c 2.4.0-ikd-1/drivers/char/keyboard.c --- 2.4.0/drivers/char/keyboard.c Thu Nov 16 15:37:39 2000 +++ 2.4.0-ikd-1/drivers/char/keyboard.c Mon Jan 15 18:40:04 2001 @@ -42,6 +42,9 @@ #include #include #include +#if defined(CONFIG_KDB) +#include +#endif #define SIZE(x) (sizeof(x)/sizeof((x)[0])) @@ -248,6 +251,13 @@ up_flag = kbd_unexpected_up(keycode); } else rep = test_and_set_bit(keycode, key_down); + +#if defined(CONFIG_KDB) + if (!up_flag && (keycode == E1_PAUSE) && kdb_on) { + kdb(KDB_REASON_KEYBOARD, 0, kbd_pt_regs); + return; + } +#endif /* CONFIG_KDB */ #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ if (keycode == SYSRQ_KEY) { diff -urN 2.4.0/drivers/char/serial.c 2.4.0-ikd-1/drivers/char/serial.c --- 2.4.0/drivers/char/serial.c Thu Dec 14 22:34:02 2000 +++ 2.4.0-ikd-1/drivers/char/serial.c Mon Jan 15 18:40:04 2001 @@ -212,6 +212,20 @@ #include #endif +#if defined(CONFIG_KDB) +#include +/* + * kdb_serial_line records the serial line number of the + * first serial console. kdb_info will be set upon receipt + * of the first ^A (which cannot happen until the port is + * opened and the interrupt handler attached). To enter + * kdb before this on a serial console-only system, you must + * use the 'kdb=early' flag to lilo and set the appropriate + * breakpoints. + */ + +static int kdb_serial_line = -1; +#endif /* CONFIG_KDB */ /* * All of the compatibilty code so we can compile serial.c against * older kernels is hidden in serial_compat.h @@ -570,6 +584,14 @@ icount = &info->state->icount; do { ch = serial_inp(info, UART_RX); +#if defined(CONFIG_KDB) + if ((info->line == kdb_serial_line) + && (ch == 1) /* CNTRL-A */ + && kdb_on) { + kdb(KDB_REASON_KEYBOARD, 0, (kdb_eframe_t)regs); + break; + } +#endif /* CONFIG_KDB */ if (tty->flip.count >= TTY_FLIPBUF_SIZE) goto ignore_char; *tty->flip.char_buf_ptr = ch; @@ -5661,6 +5683,17 @@ */ if (serial_in(info, UART_LSR) == 0xff) return -1; + +#if defined(CONFIG_KDB) + /* + * Remember the line number of the first serial + * console. We'll make this the kdb serial console too. + */ + if (kdb_serial_line == -1) { + kdb_serial_line = co->index; + kdb_port = state->port; + } +#endif /* CONFIG_KDB */ return 0; } diff -urN 2.4.0/drivers/char/sysrq.c 2.4.0-ikd-1/drivers/char/sysrq.c --- 2.4.0/drivers/char/sysrq.c Thu Dec 14 22:34:02 2000 +++ 2.4.0-ikd-1/drivers/char/sysrq.c Mon Jan 15 18:40:04 2001 @@ -6,6 +6,8 @@ * * (c) 1997 Martin Mares * based on ideas by Pavel Machek + * Add dumploGs. Keith Owens 12/04/1998. + * Add Oops, changed Off to oFf. Keith Owens 26/04/1998. */ #include @@ -25,9 +27,14 @@ #include -extern void wakeup_bdflush(int); +extern void wakeup_kswapd(int); extern void reset_vc(unsigned int); extern struct list_head super_blocks; +extern void syslog_to_console(void); +#ifdef CONFIG_TRACE +#include +extern void ktrace_to_console(void); +#endif /* Whether we react on sysrq keys or just ignore them */ int sysrq_enabled = 1; @@ -89,7 +96,7 @@ printk("Resetting\n"); machine_restart(NULL); break; - case 'o': /* O -- power off */ + case 'f': /* F -- power off */ if (sysrq_power_off) { printk("Power off\n"); sysrq_power_off(); @@ -98,12 +105,12 @@ case 's': /* S -- emergency sync */ printk("Emergency Sync\n"); emergency_sync_scheduled = EMERG_SYNC; - wakeup_bdflush(0); + wakeup_kswapd(0); break; case 'u': /* U -- emergency remount R/O */ printk("Emergency Remount R/O\n"); emergency_sync_scheduled = EMERG_REMOUNT; - wakeup_bdflush(0); + wakeup_kswapd(0); break; case 'p': /* P -- show PC */ printk("Show Regs\n"); @@ -137,6 +144,43 @@ send_sig_all(SIGKILL, 1); orig_log_level = 8; break; + case 'g': /* G -- dump all logs */ +#ifdef CONFIG_TRACE + SUSPEND_MCOUNT_TRACE; /* no point in tracing this section */ +#endif + printk("Dump All Logs\n"); + printk(KERN_INFO "DAL: syslog start\n"); + syslog_to_console(); + printk(KERN_INFO "DAL: syslog end\n"); + /* add any other log dumps here */ +#ifdef CONFIG_TRACE + printk(KERN_INFO "DAL: ktrace start\n"); + ktrace_to_console(); + printk(KERN_INFO "DAL: ktrace end\n"); + RESUME_MCOUNT_TRACE; +#endif + printk("\n"); + break; + case 'o': /* O -- oops */ + printk("Forcing Oops\n"); + *(char *)NULL = '\0'; + break; +#ifdef CONFIG_TRACE + case 'z': /* Z -- toggle trace enable */ + { + static int ktrace; + if (ktrace) { + printk("Resuming trace\n"); + RESUME_MCOUNT; + ktrace--; + } else { + ktrace++; + SUSPEND_MCOUNT; + printk("Suspending trace\n"); + } + } + break; +#endif default: /* Unknown: help */ if (kbd) printk("unRaw "); diff -urN 2.4.0/fs/proc/generic.c 2.4.0-ikd-1/fs/proc/generic.c --- 2.4.0/fs/proc/generic.c Thu Dec 14 22:34:12 2000 +++ 2.4.0-ikd-1/fs/proc/generic.c Mon Jan 15 18:40:04 2001 @@ -104,6 +104,11 @@ * return the bytes, and set `start' to the desired offset * as an unsigned int. - Paul.Russell@rustcorp.com.au */ + /* Ensure that the data will fit when using the ppos hack, + * otherwise userland receives truncated data. + */ + if (n > count-1 && start && start < page) + break; n -= copy_to_user(buf, start < page ? page : start, n); if (n == 0) { if (retval == 0) diff -urN 2.4.0/fs/proc/proc_misc.c 2.4.0-ikd-1/fs/proc/proc_misc.c --- 2.4.0/fs/proc/proc_misc.c Tue Nov 28 18:40:01 2000 +++ 2.4.0-ikd-1/fs/proc/proc_misc.c Mon Jan 15 18:40:04 2001 @@ -71,6 +71,10 @@ #ifdef CONFIG_SGI_DS1286 extern int get_ds1286_status(char *); #endif +#ifdef CONFIG_MEMLEAK +extern int memleak_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); +#endif static int proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, int len) @@ -437,6 +441,8 @@ return proc_calc_metrics(page, start, off, count, eof, len); } + + /* * This function accesses profiling information. The returned data is * binary: the sampling step and the actual contents of the profile @@ -495,11 +501,148 @@ return count; } +#ifdef CONFIG_LOCKMETER +extern ssize_t get_lockmeter_info(char *, size_t, loff_t *); +extern ssize_t put_lockmeter_info(const char *, size_t); +extern int get_lockmeter_info_size(void); + +/* + * This function accesses lock metering information. + */ +static ssize_t read_lockmeter(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + return get_lockmeter_info(buf, count, ppos); +} + +/* + * Writing to /proc/lockmeter resets the counters + */ +static ssize_t write_lockmeter(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + return put_lockmeter_info(buf, count); +} + +static struct file_operations proc_lockmeter_operations = { + read: read_lockmeter, + write: write_lockmeter, +}; +#endif /* CONFIG_LOCKMETER */ + static struct file_operations proc_profile_operations = { read: read_profile, write: write_profile, }; +#ifdef CONFIG_TRACE +#include +/* + * This function accesses kernel tracer information. The returned data is + * binary: the sampling step and the actual contents of the trace + * ringbuffer. Use of the program 'ktrace' is recommended in order to + * get meaningful info out of these data. + */ +static ssize_t read_trace(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + loff_t p = *ppos, left; + unsigned long flags; + int i; + + SUSPEND_MCOUNT_TRACE; + LOCK_MCOUNT_TRACE(flags); + + /* Calibrate the tracer */ + for (i = 1; i <= TRACE_CALIBRATION_CALLS; ++i) + mcount_internal(-1); + + UNLOCK_MCOUNT_TRACE(flags); + + if (p >= sizeof(*trace_table)) + count = 0; + else if (count > sizeof(*trace_table) - p) + count = sizeof(*trace_table) - p; + + left = copy_to_user(buf, p + (char *)trace_table, count); + + RESUME_MCOUNT_TRACE; + + if (count && left == count) + return -EFAULT; + + *ppos += count - left; + return count - left; +} + +/* + * Writing to /proc/trace resets the counters. Doesnt make much sense + * as it's a ringbuffer, but we do it anyways, it might make sense for + * doing short term traces. + */ + +static ssize_t write_trace(struct file * file, const char * buf, size_t count, loff_t *ppos) +{ + unsigned long flags; + SUSPEND_MCOUNT_TRACE; + LOCK_MCOUNT_TRACE(flags); + memset(trace_table->entries, 0, sizeof(trace_table->entries)); + trace_table->curr_call = CONFIG_TRACE_SIZE-1; + UNLOCK_MCOUNT_TRACE(flags); + RESUME_MCOUNT_TRACE; + return count; +} + +/* + * Dump the kernel trace table in hex to all registered consoles. + * A method of getting the trace table when all else fails. + * This is a raw dump, the entire table is printed in hex, 80 hex digits + * to a line. Capture the output via a serial console and feed into + * ktrace with the "-d filename" option. + * Not recommended for a large trace table over a slow serial line. + */ +#define TRACE_LINE_WIDTH 80 +void ktrace_to_console(void) +{ + static const char hexchar[] = "0123456789abcdef"; + int i; + unsigned c; + char buf[TRACE_LINE_WIDTH+3], *p; + + SUSPEND_MCOUNT_TRACE; + /* Should LOCK_MCOUNT_TRACE here but that might stop output. + * Live with the risk of dumping garbage. Cannot calibrate + * without the lock, OTOH accurate timing figures are probably + * the least of our worries at this point. + */ + + for (i = 0, p = buf; i < sizeof(*trace_table); ++i) { + /* hex convert inline, 200,000+ calls to vsprintf is slow */ + c = *((unsigned char *)(trace_table)+i); + *p++ = hexchar[c>>4]; + *p++ = hexchar[c&0xf]; + if (p - buf >= TRACE_LINE_WIDTH) { + *p++ = '\n'; + *p++ = '\0'; + console_print(buf); + p = buf; + } + } + if (p != buf) { + *p++ = '\n'; + *p++ = '\0'; + console_print(buf); + } + RESUME_MCOUNT_TRACE; +} + +static struct file_operations proc_trace_operations = { + read: read_trace, + write: write_trace, +}; + +struct proc_dir_entry *proc_root_trace; +#endif /* CONFIG_TRACE */ + struct proc_dir_entry *proc_root_kcore; void __init proc_misc_init(void) @@ -545,7 +688,10 @@ {"swaps", swaps_read_proc}, {"iomem", memory_read_proc}, {"execdomains", execdomains_read_proc}, - {NULL,} +#ifdef CONFIG_MEMLEAK + {"memleak", memleak_read_proc}, +#endif + {NULL} }; for (p = simple_ones; p->name; p++) create_proc_read_entry(p->name, 0, NULL, p->read_proc, NULL); @@ -575,8 +721,23 @@ entry->proc_fops = &ppc_htab_operations; } #endif +#ifdef CONFIG_TRACE + entry = create_proc_entry("trace", S_IWUSR | S_IRUGO, NULL); + if (entry) { + entry->proc_fops = &proc_trace_operations; + entry->size = sizeof(*trace_table); + } +#endif +#ifdef CONFIG_LOCKMETER + entry = create_proc_entry("lockmeter", S_IWUSR | S_IRUGO, NULL); + if (entry) { + entry->proc_fops = &proc_lockmeter_operations; + entry->size = get_lockmeter_info_size(); + } +#endif entry = create_proc_read_entry("slabinfo", S_IWUSR | S_IRUGO, NULL, slabinfo_read_proc, NULL); if (entry) entry->write_proc = slabinfo_write_proc; } + diff -urN 2.4.0/include/asm-alpha/kdb.h 2.4.0-ikd-1/include/asm-alpha/kdb.h --- 2.4.0/include/asm-alpha/kdb.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-alpha/kdb.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,10 @@ +/* + * Dummy include/asm/kdb.h for alpha. + */ +#if !defined(_ASM_KDB_H) +#define _ASM_KDB_H +#define KDB_ENTER() +struct pt_regs; +typedef struct pt_regs *kdb_eframe_t; +typedef unsigned long kdb_machreg_t; +#endif /* ASM_KDB_H */ diff -urN 2.4.0/include/asm-alpha/lockmeter.h 2.4.0-ikd-1/include/asm-alpha/lockmeter.h --- 2.4.0/include/asm-alpha/lockmeter.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-alpha/lockmeter.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,60 @@ +/* + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.h by Jack Steiner (steiner@sgi.com) + * + * Modified by Peter Rival (frival@zk3.dec.com) + */ + +#ifndef _ALPHA_LOCKMETER_H +#define _ALPHA_LOCKMETER_H + +#include +#define CPU_CYCLE_FREQUENCY hwrpb->cycle_freq + +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#define local_irq_save(x) \ + __save_and_cli(x) +#define local_irq_restore(x) \ + __restore_flags(x) +#endif /* Linux version 2.2.x */ + +#define SPINLOCK_MAGIC_INIT /**/ + +extern __inline__ long nonmetered_read_trylock(rwlock_t *lock) +{ + long temp,result; + + __asm__ __volatile__( + " ldl_l %1,%0\n" + " mov $31,%2\n" + " blbs %1,1f\n" + " subl %1,2,%2\n" + " stl_c %2,%0\n" + "1: mb\n" + : "=m" (__dummy_lock(lock)), "=&r" (temp), "=&r" (result) + : "m" (__dummy_lock(lock)) + ); + return (result); +} + +extern __inline__ long nonmetered_write_trylock(rwlock_t *lock) +{ + long temp,result; + + __asm__ __volatile__( + " ldl_l %1,%0\n" + " mov $31,%2\n" + " bne %1,1f\n" + " or $31,1,%2\n" + " stl_c %2,%0\n" + "1: mb\n" + : "=m" (__dummy_lock(lock)), "=&r" (temp), "=&r" (result) + : "m" (__dummy_lock(lock)) + ); + + return (result); +} + +#endif /* _ALPHA_LOCKMETER_H */ + diff -urN 2.4.0/include/asm-alpha/pgtable.h 2.4.0-ikd-1/include/asm-alpha/pgtable.h --- 2.4.0/include/asm-alpha/pgtable.h Mon Dec 18 20:06:11 2000 +++ 2.4.0-ikd-1/include/asm-alpha/pgtable.h Mon Jan 15 18:40:04 2001 @@ -313,7 +313,7 @@ #define kern_addr_valid(addr) (1) #define io_remap_page_range(start, busaddr, size, prot) \ - remap_page_range(start, virt_to_phys(__ioremap(busaddr)), size, prot) + remap_page_range(start, virt_to_phys(__ioremap(busaddr), 0), size, prot) #define pte_ERROR(e) \ printk("%s:%d: bad pte %016lx.\n", __FILE__, __LINE__, pte_val(e)) diff -urN 2.4.0/include/asm-alpha/profiler.h 2.4.0-ikd-1/include/asm-alpha/profiler.h --- 2.4.0/include/asm-alpha/profiler.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-alpha/profiler.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,51 @@ +#ifndef _LINUX_PROFILER_ASM_H +#define _LINUX_PROFILER_ASM_H + +#include + +#ifdef CONFIG_DEBUG_MCOUNT + +/* + * You've got to define two macros if you port the profiling stuff: + */ + +/* + * [kernel stack overflow profiling] + * + * this says how much kernel stack space is >left<. If this goes + * below a certain treshold then we generate an artificial oops. + * + * we do not assume anything about stack growth direction + */ + +#define get_stack_left() \ +({ \ + register unsigned long sp; \ + asm("bis $30,$30,%0" : "=r" (sp)); \ + sp & ~(PAGE_MASK << 1) - sizeof(struct task_struct); \ +}) + +/* + * [kernel tracer] + * + * this macro gets fast an accurate time and puts it into a 'u32' + * variable. It's used as a tracer timestamp. + */ + +#ifdef CONFIG_TRACE_TIMESTAMP +#define get_profiler_timestamp() \ + ( { \ + register u32 __res; \ + asm volatile ("rpcc %0" : "=r" (__res)); \ + __res; \ + } ) + +/* Always u32, even when CONFIG_TRACE_TRUNCTIME */ +typedef u32 profiler_timestamp_t; +#endif /* CONFIG_TRACE_TIMESTAMP */ + +typedef unsigned long profiler_pc_t; + +#endif /* CONFIG_DEBUG_MCOUNT */ + +#endif /* _LINUX_PROFILER_ASM_H */ diff -urN 2.4.0/include/asm-alpha/spinlock.h 2.4.0-ikd-1/include/asm-alpha/spinlock.h --- 2.4.0/include/asm-alpha/spinlock.h Mon Dec 18 20:06:11 2000 +++ 2.4.0-ikd-1/include/asm-alpha/spinlock.h Mon Jan 15 18:40:04 2001 @@ -55,6 +55,7 @@ (LOCK)->lock ? "taken" : "freed", (LOCK)->on_cpu); \ } while (0) #else +#ifndef CONFIG_LOCKMETER static inline void spin_unlock(spinlock_t * lock) { mb(); @@ -86,7 +87,43 @@ #define spin_trylock(lock) (!test_and_set_bit(0,(lock))) #define spin_lock_own(LOCK, LOCATION) ((void)0) -#endif /* DEBUG_SPINLOCK */ + +#else /* CONFIG_LOCKMETER */ +extern void _spin_lock_(void *lock_ptr); +# define spin_lock(lock) _spin_lock_(lock) +static inline void nonmetered_spin_lock(void *lock) { + long tmp; + /* Use sub-sections to put the actual loop at the end + of this object file's text section so as to perfect + branch prediction. */ + __asm__ __volatile__( + "1: ldl_l %0,%1\n" + " blbs %0,2f\n" + " or %0,1,%0\n" + " stl_c %0,%1\n" + " beq %0,2f\n" + " mb\n" + ".section .text2,\"ax\"\n" + "2: ldl %0,%1\n" + " blbs %0,2b\n" + " br 1b\n" + ".previous" + : "=r" (tmp), "=m" (__dummy_lock(lock)) + : "m"(__dummy_lock(lock))); +} + +extern void _spin_unlock_(void *lock_ptr); +#define spin_unlock(lock) _spin_unlock_(lock) +static inline void nonmetered_spin_unlock(spinlock_t *lock) { + mb(); + lock->lock = 0; +} +extern __inline__ int _spin_trylock_(void *); +#define spin_trylock(lock) _spin_trylock_(lock) +#define nonmetered_spin_trylock(lock) (!test_and_set_bit(0,(lock))) +#define nonmetered_spin_testlock(lock) test_bit(0,(lock)) +#endif /* !CONFIG_LOCKMETER */ +#endif/* DEBUG_SPINLOCK */ /***********************************************************/ @@ -99,7 +136,8 @@ #if DEBUG_RWLOCK extern void write_lock(rwlock_t * lock); extern void read_lock(rwlock_t * lock); -#else +#else /* DEBUG_RWLOCK */ +#ifndef CONFIG_LOCKMETER static inline void write_lock(rwlock_t * lock) { long regx; @@ -139,6 +177,15 @@ : "=m" (*(volatile int *)lock), "=&r" (regx) : "m" (*(volatile int *)lock) : "memory"); } + +# else /* CONFIG_LOCKMETER */ + +extern void _read_lock_(rwlock_t *rw); +#define read_lock(rw) _read_lock_(rw) +extern void _write_lock_(rwlock_t *rw); +#define write_lock(rw) _write_lock_(rw) + +#endif /* !CONFIG_LOCKMETER */ #endif /* DEBUG_RWLOCK */ static inline void write_unlock(rwlock_t * lock) diff -urN 2.4.0/include/asm-arm/kdb.h 2.4.0-ikd-1/include/asm-arm/kdb.h --- 2.4.0/include/asm-arm/kdb.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-arm/kdb.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,10 @@ +/* + * Dummy include/asm/kdb.h for arm. + */ +#if !defined(_ASM_KDB_H) +#define _ASM_KDB_H +#define KDB_ENTER() +struct pt_regs; +typedef struct pt_regs *kdb_eframe_t; +typedef unsigned long kdb_machreg_t; +#endif /* ASM_KDB_H */ diff -urN 2.4.0/include/asm-generic/kdb.h 2.4.0-ikd-1/include/asm-generic/kdb.h --- 2.4.0/include/asm-generic/kdb.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-generic/kdb.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,10 @@ +/* + * Dummy include/asm/kdb.h for generic. + */ +#if !defined(_ASM_KDB_H) +#define _ASM_KDB_H +#define KDB_ENTER() +struct pt_regs; +typedef struct pt_regs *kdb_eframe_t; +typedef unsigned long kdb_machreg_t; +#endif /* ASM_KDB_H */ diff -urN 2.4.0/include/asm-i386/apic.h 2.4.0-ikd-1/include/asm-i386/apic.h --- 2.4.0/include/asm-i386/apic.h Sun Jan 14 16:33:41 2001 +++ 2.4.0-ikd-1/include/asm-i386/apic.h Mon Jan 15 18:40:04 2001 @@ -2,6 +2,7 @@ #define __ASM_APIC_H #include +#include #include #include @@ -75,6 +76,67 @@ extern void init_apic_mappings(void); extern void smp_local_timer_interrupt(struct pt_regs * regs); extern void setup_APIC_clocks(void); -#endif + +/* NMI watchdog is driven from the IO-APIC or from the local APIC. NMI + * related data has to be defined somewhere, may as well be here. + * Keith Owens. + */ + +extern int nmi_watchdog, proc_nmi_watchdog, nmi_watchdog_source; +extern int set_nmi_watchdog(int); +extern int setup_apic_nmi_watchdog(int); +extern int set_nmi_counter_local(void); + +#endif /* CONFIG_X86_LOCAL_APIC */ + +/* + * {rd,wr}msr_eio by HPA, moved from arch/i386/msr.c to here. + * Keith Owens. + */ + +/* Note: "err" is handled in a funny way below. Otherwise one version + of gcc or another breaks. */ + +extern inline int wrmsr_eio(u32 reg, u32 eax, u32 edx) +{ + int err; + + asm volatile( + "1: wrmsr\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3: movl %4,%0\n" + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b,3b\n" + ".previous" + : "=&bDS" (err) + : "a" (eax), "d" (edx), "c" (reg), "i" (-EIO), "0" (0)); + + return err; +} + +extern inline int rdmsr_eio(u32 reg, u32 *eax, u32 *edx) +{ + int err; + + asm volatile( + "1: rdmsr\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3: movl %4,%0\n" + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 4\n" + " .long 1b,3b\n" + ".previous" + : "=&bDS" (err), "=a" (*eax), "=d" (*edx) + : "c" (reg), "i" (-EIO), "0" (0)); + + return err; +} #endif diff -urN 2.4.0/include/asm-i386/hw_irq.h 2.4.0-ikd-1/include/asm-i386/hw_irq.h --- 2.4.0/include/asm-i386/hw_irq.h Mon Jan 15 01:55:12 2001 +++ 2.4.0-ikd-1/include/asm-i386/hw_irq.h Mon Jan 15 18:40:04 2001 @@ -22,6 +22,7 @@ #define FIRST_EXTERNAL_VECTOR 0x20 #define SYSCALL_VECTOR 0x80 +#define KDBENTER_VECTOR 0x81 /* * Vectors 0x20-0x2f are used for ISA interrupts. @@ -41,6 +42,7 @@ #define INVALIDATE_TLB_VECTOR 0xfd #define RESCHEDULE_VECTOR 0xfc #define CALL_FUNCTION_VECTOR 0xfb +#define KDB_VECTOR 0xfa /* * Local APIC timer IRQ vector is on a different priority level, diff -urN 2.4.0/include/asm-i386/kdb.h 2.4.0-ikd-1/include/asm-i386/kdb.h --- 2.4.0/include/asm-i386/kdb.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-i386/kdb.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,66 @@ +/* + * Minimalist Kernel Debugger + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + */ +#if !defined(_ASM_KDB_H) +#define _ASM_KDB_H + +#include + + /* + * KDB_ENTER() is a macro which causes entry into the kernel + * debugger from any point in the kernel code stream. If it + * is intended to be used from interrupt level, it must use + * a non-maskable entry method. + */ +#ifdef CONFIG_KDB +#define KDB_ENTER() \ + do { \ + SUSPEND_MCOUNT; \ + asm __volatile__ ("\tint $129\n"); \ + RESUME_MCOUNT; \ + } while(0) +#else +#define KDB_ENTER() do {} while(0) +#endif + + /* + * Define the exception frame for this architeture + */ +struct pt_regs; +typedef struct pt_regs *kdb_eframe_t; + + /* + * Needed for exported symbols. + */ +typedef unsigned long kdb_machreg_t; + +#define kdb_machreg_fmt "0x%lx" +#define kdb_machreg_fmt0 "0x%08lx" +#define kdb_bfd_vma_fmt "0x%lx" +#define kdb_bfd_vma_fmt0 "0x%08lx" +#define kdb_elfw_addr_fmt "0x%x" +#define kdb_elfw_addr_fmt0 "0x%08x" + + /* + * Per cpu arch specific kdb state. Must be in range 0xff000000. + */ +#define KDB_STATE_A_IF 0x01000000 /* Saved IF flag */ + +#endif /* ASM_KDB_H */ diff -urN 2.4.0/include/asm-i386/kdbprivate.h 2.4.0-ikd-1/include/asm-i386/kdbprivate.h --- 2.4.0/include/asm-i386/kdbprivate.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-i386/kdbprivate.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,179 @@ +/* + * Minimalist Kernel Debugger + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ +#if !defined(_ASM_KDBPRIVATE_H) +#define _ASM_KDBPRIVATE_H + +typedef unsigned char kdb_machinst_t; + + /* + * KDB_MAXBPT describes the total number of breakpoints + * supported by this architecure. + */ +#define KDB_MAXBPT 16 + /* + * KDB_MAXHARDBPT describes the total number of hardware + * breakpoint registers that exist. + */ +#define KDB_MAXHARDBPT 4 + /* + * Provide space for KDB_MAX_COMMANDS commands. + */ +#define KDB_MAX_COMMANDS 125 + + /* + * Platform specific environment entries + */ +#define KDB_PLATFORM_ENV "IDMODE=x86", "BYTESPERWORD=4", "IDCOUNT=16" + + /* + * Define the direction that the stack grows + */ +#define KDB_STACK_DIRECTION -1 /* Stack grows down */ + + /* + * Support for ia32 debug registers + */ +typedef struct _kdbhard_bp { + kdb_machreg_t bph_reg; /* Register this breakpoint uses */ + + unsigned int bph_free:1; /* Register available for use */ + unsigned int bph_data:1; /* Data Access breakpoint */ + + unsigned int bph_write:1; /* Write Data breakpoint */ + unsigned int bph_mode:2; /* 0=inst, 1=write, 2=io, 3=read */ + unsigned int bph_length:2; /* 0=1, 1=2, 2=BAD, 3=4 (bytes) */ +} kdbhard_bp_t; + +extern kdbhard_bp_t kdb_hardbreaks[/* KDB_MAXHARDBPT */]; + +#define IA32_BREAKPOINT_INSTRUCTION 0xcc + +#define DR6_BT 0x00008000 +#define DR6_BS 0x00004000 +#define DR6_BD 0x00002000 + +#define DR6_B3 0x00000008 +#define DR6_B2 0x00000004 +#define DR6_B1 0x00000002 +#define DR6_B0 0x00000001 + +#define DR7_RW_VAL(dr, drnum) \ + (((dr) >> (16 + (4 * (drnum)))) & 0x3) + +#define DR7_RW_SET(dr, drnum, rw) \ + do { \ + (dr) &= ~(0x3 << (16 + (4 * (drnum)))); \ + (dr) |= (((rw) & 0x3) << (16 + (4 * (drnum)))); \ + } while (0) + +#define DR7_RW0(dr) DR7_RW_VAL(dr, 0) +#define DR7_RW0SET(dr,rw) DR7_RW_SET(dr, 0, rw) +#define DR7_RW1(dr) DR7_RW_VAL(dr, 1) +#define DR7_RW1SET(dr,rw) DR7_RW_SET(dr, 1, rw) +#define DR7_RW2(dr) DR7_RW_VAL(dr, 2) +#define DR7_RW2SET(dr,rw) DR7_RW_SET(dr, 2, rw) +#define DR7_RW3(dr) DR7_RW_VAL(dr, 3) +#define DR7_RW3SET(dr,rw) DR7_RW_SET(dr, 3, rw) + + +#define DR7_LEN_VAL(dr, drnum) \ + (((dr) >> (18 + (4 * (drnum)))) & 0x3) + +#define DR7_LEN_SET(dr, drnum, rw) \ + do { \ + (dr) &= ~(0x3 << (18 + (4 * (drnum)))); \ + (dr) |= (((rw) & 0x3) << (18 + (4 * (drnum)))); \ + } while (0) +#define DR7_LEN0(dr) DR7_LEN_VAL(dr, 0) +#define DR7_LEN0SET(dr,len) DR7_LEN_SET(dr, 0, len) +#define DR7_LEN1(dr) DR7_LEN_VAL(dr, 1) +#define DR7_LEN1SET(dr,len) DR7_LEN_SET(dr, 1, len) +#define DR7_LEN2(dr) DR7_LEN_VAL(dr, 2) +#define DR7_LEN2SET(dr,len) DR7_LEN_SET(dr, 2, len) +#define DR7_LEN3(dr) DR7_LEN_VAL(dr, 3) +#define DR7_LEN3SET(dr,len) DR7_LEN_SET(dr, 3, len) + +#define DR7_G0(dr) (((dr)>>1)&0x1) +#define DR7_G0SET(dr) ((dr) |= 0x2) +#define DR7_G0CLR(dr) ((dr) &= ~0x2) +#define DR7_G1(dr) (((dr)>>3)&0x1) +#define DR7_G1SET(dr) ((dr) |= 0x8) +#define DR7_G1CLR(dr) ((dr) &= ~0x8) +#define DR7_G2(dr) (((dr)>>5)&0x1) +#define DR7_G2SET(dr) ((dr) |= 0x20) +#define DR7_G2CLR(dr) ((dr) &= ~0x20) +#define DR7_G3(dr) (((dr)>>7)&0x1) +#define DR7_G3SET(dr) ((dr) |= 0x80) +#define DR7_G3CLR(dr) ((dr) &= ~0x80) + +#define DR7_L0(dr) (((dr))&0x1) +#define DR7_L0SET(dr) ((dr) |= 0x1) +#define DR7_L0CLR(dr) ((dr) &= ~0x1) +#define DR7_L1(dr) (((dr)>>2)&0x1) +#define DR7_L1SET(dr) ((dr) |= 0x4) +#define DR7_L1CLR(dr) ((dr) &= ~0x4) +#define DR7_L2(dr) (((dr)>>4)&0x1) +#define DR7_L2SET(dr) ((dr) |= 0x10) +#define DR7_L2CLR(dr) ((dr) &= ~0x10) +#define DR7_L3(dr) (((dr)>>6)&0x1) +#define DR7_L3SET(dr) ((dr) |= 0x40) +#define DR7_L3CLR(dr) ((dr) &= ~0x40) + +#define DR7_GD 0x00002000 /* General Detect Enable */ +#define DR7_GE 0x00000200 /* Global exact */ +#define DR7_LE 0x00000100 /* Local exact */ + +extern kdb_machreg_t kdb_getdr6(void); +extern void kdb_putdr6(kdb_machreg_t); + +extern kdb_machreg_t kdb_getdr7(void); +extern void kdb_putdr7(kdb_machreg_t); + +extern kdb_machreg_t kdb_getdr(int); +extern void kdb_putdr(int, kdb_machreg_t); + +extern kdb_machreg_t kdb_getcr(int); + +#define KDB_HAVE_LONGJMP +#ifdef KDB_HAVE_LONGJMP +/* + * Support for setjmp/longjmp + */ +#define JB_BX 0 +#define JB_SI 1 +#define JB_DI 2 +#define JB_BP 3 +#define JB_SP 4 +#define JB_PC 5 + +typedef struct __kdb_jmp_buf { + unsigned long regs[6]; /* kdb_setjmp assumes fixed offsets here */ +} kdb_jmp_buf; + +extern int kdb_setjmp(kdb_jmp_buf *); +extern void kdb_longjmp(kdb_jmp_buf *, int); + +extern kdb_jmp_buf kdbjmpbuf[]; +#endif /* KDB_HAVE_LONGJMP */ + +#endif /* !_ASM_KDBPRIVATE_H */ diff -urN 2.4.0/include/asm-i386/keyboard.h 2.4.0-ikd-1/include/asm-i386/keyboard.h --- 2.4.0/include/asm-i386/keyboard.h Mon Jan 15 01:55:47 2001 +++ 2.4.0-ikd-1/include/asm-i386/keyboard.h Mon Jan 15 18:40:04 2001 @@ -38,6 +38,7 @@ #define kbd_sysrq_xlate pckbd_sysrq_xlate #define SYSRQ_KEY 0x54 +#define E1_PAUSE 119 /* PAUSE key */ /* resource allocation */ #define kbd_request_region() diff -urN 2.4.0/include/asm-i386/lockmeter.h 2.4.0-ikd-1/include/asm-i386/lockmeter.h --- 2.4.0/include/asm-i386/lockmeter.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-i386/lockmeter.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,32 @@ +/* + * Copyright (C) 1999,2000 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.h by Jack Steiner (steiner@sgi.com) + */ + +#ifndef _I386_LOCKMETER_H +#define _I386_LOCKMETER_H + +extern unsigned long cpu_khz; +#define CPU_CYCLE_FREQUENCY (cpu_khz * 1000) + +#define save_and_cli(flags) do {save_flags(flags); cli();} while(0) + +/* + * macros to cache and retrieve an index value inside of a lock + * these macros assume that there are less than 65536 simultaneous + * (read mode) holders of a rwlock. + * we also assume that the hash table has less than 32767 entries. + * the high order bit is used for write locking a rw_lock + */ +#define INDEX_MASK 0x7FFF0000 +#define READERS_MASK 0x0000FFFF +#define INDEX_SHIFT 16 +#define PUT_INDEX(lock,index) \ + lock = (((lock) & ~INDEX_MASK) | (index) << INDEX_SHIFT) +#define GET_INDEX(lock) \ + (((lock) & INDEX_MASK) >> INDEX_SHIFT) + +#endif /* _I386_LOCKMETER_H */ + diff -urN 2.4.0/include/asm-i386/msr.h 2.4.0-ikd-1/include/asm-i386/msr.h --- 2.4.0/include/asm-i386/msr.h Thu Dec 14 22:34:12 2000 +++ 2.4.0-ikd-1/include/asm-i386/msr.h Mon Jan 15 18:40:04 2001 @@ -34,3 +34,22 @@ #define MSR_IA32_PLATFORM_ID 0x17 #define MSR_IA32_UCODE_WRITE 0x79 #define MSR_IA32_UCODE_REV 0x8B + +#define MCG_STATUS_MSR 0x17a +#define MCG_CAP 0x179 +#define MCG_CTL 0x17b +#define MC0_BASE 0x400 +#define MC0_CTL_OFFSET 0x0 +#define MC0_STATUS_OFFSET 0x1 +#define MC0_ADDR_OFFSET 0x2 +#define MC0_MISC_OFFSET 0x3 +#define MC0_BANK_COUNT 0x4 +#define DEBUGCTLMSR 0x1d9 +#define LASTBRANCHFROMIP 0x1db +#define LASTBRANCHTOIP 0x1dc +#define LASTINTFROMIP 0x1dd +#define LASTINTTOIP 0x1de +#define PERFCTR0 0xc1 +#define PERFCTR1 0xc2 +#define EVNTSEL0 0x186 +#define EVNTSEL1 0x187 diff -urN 2.4.0/include/asm-i386/pgalloc.h 2.4.0-ikd-1/include/asm-i386/pgalloc.h --- 2.4.0/include/asm-i386/pgalloc.h Mon Jan 15 01:55:12 2001 +++ 2.4.0-ikd-1/include/asm-i386/pgalloc.h Mon Jan 15 18:40:04 2001 @@ -23,6 +23,7 @@ * if any. */ +#ifndef CONFIG_MEMLEAK extern __inline__ pgd_t *get_pgd_slow(void) { pgd_t *ret = (pgd_t *)__get_free_page(GFP_KERNEL); @@ -92,11 +93,143 @@ free_page((unsigned long)pte); } +#else /* CONFIG_MEMLEAK */ + +#if CONFIG_X86_PAE +#define get_pgd_slow() \ +({ \ + pgd_t *_ret = (pgd_t *) __get_free_page(GFP_KERNEL); \ + if (_ret) { \ + int i; \ + for (i = 0; i < USER_PTRS_PER_PGD; i++) \ + __pgd_clear(ret + i); \ + memcpy (_ret + USER_PTRS_PER_PGD, swapper_pg_dir + USER_PTRS_PER_PGD, \ + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); \ + } \ + _ret; \ +}) +#else +#define get_pgd_slow() \ +({ \ + pgd_t *_ret = (pgd_t *) __get_free_page(GFP_KERNEL); \ + if (_ret) { \ + memset (_ret, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); \ + memcpy (_ret + USER_PTRS_PER_PGD, swapper_pg_dir + USER_PTRS_PER_PGD, \ + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); \ + } \ + _ret; \ +}) +#endif + +#define get_pgd_fast() \ +({ \ + unsigned long *_ret; \ + if((_ret = pgd_quicklist) != NULL) { \ + pgd_quicklist = (unsigned long *)(*_ret); \ + _ret[0] = 0; \ + pgtable_cache_size--; \ + } else \ + _ret = (unsigned long *)get_pgd_slow(); \ + (pgd_t *)_ret; \ +}) + +#define free_pgd_fast(pgd) \ +({ \ + *(unsigned long *)(pgd) = (unsigned long) pgd_quicklist; \ + pgd_quicklist = (unsigned long *) (pgd); \ + pgtable_cache_size++; \ +}) + +#define free_pgd_slow(pgd) \ +({ \ + free_page((unsigned long)(pgd)); \ +}) + +/* arch/i386/mm/init.c */ +extern pte_t * get_bad_pte_table(void); + +#define get_pte_slow(pmd, offset) \ +({ \ + pte_t *_ret; \ + unsigned long _pte = (unsigned long) __get_free_page(GFP_KERNEL); \ + if (pmd_none(*((pmd_t *)(pmd)))) { \ + if (_pte) { \ + clear_page((void *)_pte); \ + pmd_val(*((pmd_t *)(pmd))) = _PAGE_TABLE + __pa(_pte); \ + _ret = (pte_t *)(_pte + (unsigned long)(offset)); \ + goto out_get_pte_slow; \ + } \ + pmd_val(*((pmd_t *)(pmd))) = _PAGE_TABLE + __pa(get_bad_pte_table()); \ + _ret = NULL; \ + goto out_get_pte_slow; \ + } \ + free_page(_pte); \ + if (pmd_bad(*((pmd_t *)(pmd)))) { \ + __handle_bad_pmd((pmd_t *)(pmd)); \ + _ret = NULL; \ + goto out_get_pte_slow; \ + } \ + _ret = (pte_t *) (pmd_page(*((pmd_t *)(pmd))) + (unsigned long)(offset)); \ +out_get_pte_slow: \ + _ret; \ +}) + +#define get_pte_kernel_slow(pmd, offset) \ +({ \ + pte_t *_ret, *_pte = (pte_t *) __get_free_page(GFP_KERNEL); \ + if (pmd_none(*((pmd_t *)(pmd)))) { \ + if (_pte) { \ + clear_page((unsigned long)_pte); \ + pmd_val(*((pmd_t *)(pmd))) = _KERNPG_TABLE + __pa(_pte); \ + _ret = _pte + (unsigned long)(offset); \ + goto out_get_pte_kernel_slow; \ + } \ + pmd_val(*((pmd_t *)(pmd))) = _KERNPG_TABLE + __pa(get_bad_pte_table()); \ + _ret = NULL; \ + goto out_get_pte_kernel_slow; \ + } \ + free_page((unsigned long)_pte); \ + if (pmd_bad(*((pmd_t *)(pmd)))) { \ + __handle_bad_pmd_kernel((pmd_t *)(pmd)); \ + _ret = NULL; \ + goto out_get_pte_kernel_slow; \ + } \ + _ret = (pte_t *)(pmd_page(*((pmd_t *)(pmd))) + (unsigned long)(offset)); \ +out_get_pte_kernel_slow: \ + _ret; \ +}) + +#define get_pte_fast() \ +({ \ + unsigned long *_ret; \ + if((_ret = (unsigned long *)pte_quicklist) != NULL) { \ + pte_quicklist = (unsigned long *)(*_ret); \ + _ret[0] = _ret[1]; \ + pgtable_cache_size--; \ + } \ + (pte_t *)_ret; \ +}) + +#define free_pte_fast(pte) \ +({ \ + *(unsigned long *)(pte) = (unsigned long) pte_quicklist; \ + pte_quicklist = (unsigned long *) (pte); \ + pgtable_cache_size++; \ +}) + +#define free_pte_slow(pte) \ +({ \ + free_page((unsigned long)(pte)); \ +}) + +#endif /* CONFIG_MEMLEAK */ + #define pte_free_kernel(pte) free_pte_slow(pte) #define pte_free(pte) free_pte_slow(pte) #define pgd_free(pgd) free_pgd_slow(pgd) #define pgd_alloc() get_pgd_fast() +#ifndef CONFIG_MEMLEAK extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address) { if (!pmd) @@ -139,6 +272,61 @@ __handle_bad_pmd(pmd); return NULL; } +#else /* CONFIG_MEMLEAK */ +#define pte_alloc_kernel(pmd, address) \ +({ \ + pte_t * _ret; \ + unsigned long _address = ((unsigned long)(address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); \ + if (!(pmd)) \ + BUG(); \ + if (pmd_none(*((pmd_t *)(pmd)))) { \ + pte_t * _page = (pte_t *) get_pte_fast(); \ + if (!_page) { \ + _ret = get_pte_kernel_slow(((pmd_t *)(pmd)), _address); \ + goto out_pte_alloc_kernel; \ + } \ + pmd_val(*((pmd_t *)(pmd))) = _KERNPG_TABLE + __pa(_page); \ + _ret = _page + _address; \ + goto out_pte_alloc_kernel; \ + } \ + if (pmd_bad(*((pmd_t *)(pmd)))) { \ + __handle_bad_pmd_kernel(((pmd_t *)(pmd))); \ + _ret = NULL; \ + goto out_pte_alloc_kernel; \ + } \ + _ret = (pte_t *) pmd_page(*((pmd_t *)(pmd))) + _address; \ +out_pte_alloc_kernel: \ + _ret; \ +}) + +#define pte_alloc(pmd, address) \ +({ \ + pte_t *_ret; \ + unsigned long _address = ((unsigned long)(address) >> (PAGE_SHIFT-2)) & 4*(PTRS_PER_PTE - 1); \ + if (pmd_none(*((pmd_t *)(pmd)))) \ + goto getnew; \ + if (pmd_bad(*((pmd_t *)(pmd)))) \ + goto fix; \ + _ret = (pte_t *) (pmd_page(*((pmd_t *)(pmd))) + _address); \ + goto out_pte_alloc; \ +getnew: \ +{ \ + unsigned long _page = (unsigned long) get_pte_fast(); \ + if (!_page) { \ + _ret = get_pte_slow(((pmd_t *)(pmd)), _address); \ + goto out_pte_alloc; \ + } \ + pmd_val(*((pmd_t *)(pmd))) = _PAGE_TABLE + __pa(_page); \ + _ret = (pte_t *) (_page + _address); \ + goto out_pte_alloc; \ +} \ +fix: \ + __handle_bad_pmd(((pmd_t *)(pmd))); \ + _ret = NULL; \ +out_pte_alloc: \ + _ret; \ +}) +#endif /* CONFIG_MEMLEAK */ /* * allocating and freeing a pmd is trivial: the 1-entry pmd is diff -urN 2.4.0/include/asm-i386/pgtable.h 2.4.0-ikd-1/include/asm-i386/pgtable.h --- 2.4.0/include/asm-i386/pgtable.h Mon Jan 8 06:32:58 2001 +++ 2.4.0-ikd-1/include/asm-i386/pgtable.h Mon Jan 15 18:40:04 2001 @@ -136,7 +136,11 @@ * The vmalloc() routines leaves a hole of 4kB between each vmalloced * area for the same reason. ;) */ +#ifndef CONFIG_MEMLEAK #define VMALLOC_OFFSET (8*1024*1024) +#else +#define VMALLOC_OFFSET (128*1024*1024) +#endif #define VMALLOC_START (((unsigned long) high_memory + 2*VMALLOC_OFFSET-1) & \ ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) diff -urN 2.4.0/include/asm-i386/profiler.h 2.4.0-ikd-1/include/asm-i386/profiler.h --- 2.4.0/include/asm-i386/profiler.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-i386/profiler.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,62 @@ +#ifndef _LINUX_PROFILER_ASM_H +#define _LINUX_PROFILER_ASM_H + +#include + +#ifdef CONFIG_DEBUG_MCOUNT + +/* + * You've got to define two macros if you port the profiling stuff: + */ + +/* + * [kernel stack overflow profiling] + * + * this says how much kernel stack space is >left<. If this goes + * below a certain treshold then we generate an artificial oops. + * + * we do not assume anything about stack growth direction + */ + +#define get_stack_left() \ +({ \ + register unsigned long __res; \ + __asm__("movl %%esp, %0" : "=r" (__res)); \ + (__res & ~(PAGE_MASK << 1)) - sizeof(struct task_struct); \ +}) + +/* + * [kernel tracer] + * + * this macro gets fast an accurate time and puts it into a 'long long' + * variable. It's used as a tracer timestamp. + */ + +#ifdef CONFIG_TRACE_TIMESTAMP +#define get_profiler_timestamp() \ + ( { \ + register u64 __res; \ + if (test_bit(X86_FEATURE_TSC, boot_cpu_data.x86_capability)) { \ + __asm__ __volatile__( \ + "rdtsc" : "=A"(__res) \ + ); \ + } \ + else { \ + /* no rdtsc, use jiffies instead */ \ + __res = jiffies; \ + } \ + __res; \ + } ) + +#ifdef CONFIG_TRACE_TRUNCTIME +typedef u32 profiler_timestamp_t; +#else +typedef u64 profiler_timestamp_t; +#endif /* CONFIG_TRACE_TRUNCTIME */ +#endif /* CONFIG_TRACE_TIMESTAMP */ + +typedef unsigned long profiler_pc_t; + +#endif /* CONFIG_DEBUG_MCOUNT */ + +#endif /* _LINUX_PROFILER_ASM_H */ diff -urN 2.4.0/include/asm-i386/ptrace.h 2.4.0-ikd-1/include/asm-i386/ptrace.h --- 2.4.0/include/asm-i386/ptrace.h Thu Nov 16 15:37:42 2000 +++ 2.4.0-ikd-1/include/asm-i386/ptrace.h Mon Jan 15 18:40:04 2001 @@ -54,6 +54,29 @@ /* options set using PTRACE_SETOPTIONS */ #define PTRACE_O_TRACESYSGOOD 0x00000001 +enum EFLAGS { + EF_CF = 0x00000001, + EF_PF = 0x00000004, + EF_AF = 0x00000010, + EF_ZF = 0x00000040, + EF_SF = 0x00000080, + EF_TF = 0x00000100, + EF_IE = 0x00000200, + EF_DF = 0x00000400, + EF_OF = 0x00000800, + EF_IOPL = 0x00003000, + EF_IOPL_RING0 = 0x00000000, + EF_IOPL_RING1 = 0x00001000, + EF_IOPL_RING2 = 0x00002000, + EF_NT = 0x00004000, /* nested task */ + EF_RF = 0x00010000, /* resume */ + EF_VM = 0x00020000, /* virtual mode */ + EF_AC = 0x00040000, /* alignment */ + EF_VIF = 0x00080000, /* virtual interrupt */ + EF_VIP = 0x00100000, /* virtual interrupt pending */ + EF_ID = 0x00200000, /* id */ +}; + #ifdef __KERNEL__ #define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->xcs)) #define instruction_pointer(regs) ((regs)->eip) diff -urN 2.4.0/include/asm-i386/smplock.h 2.4.0-ikd-1/include/asm-i386/smplock.h --- 2.4.0/include/asm-i386/smplock.h Mon Jan 15 01:55:12 2001 +++ 2.4.0-ikd-1/include/asm-i386/smplock.h Mon Jan 15 18:40:04 2001 @@ -42,7 +42,7 @@ */ extern __inline__ void lock_kernel(void) { -#if 1 +#if 1 || defined(CONFIG_LOCKMETER) if (!++current->lock_depth) spin_lock(&kernel_flag); #else @@ -60,7 +60,7 @@ { if (current->lock_depth < 0) BUG(); -#if 1 +#if 1 || defined(CONFIG_LOCKMETER) if (--current->lock_depth < 0) spin_unlock(&kernel_flag); #else diff -urN 2.4.0/include/asm-i386/spinlock.h 2.4.0-ikd-1/include/asm-i386/spinlock.h --- 2.4.0/include/asm-i386/spinlock.h Mon Jan 15 01:55:12 2001 +++ 2.4.0-ikd-1/include/asm-i386/spinlock.h Mon Jan 15 18:40:04 2001 @@ -65,7 +65,11 @@ #define spin_unlock_string \ "movb $1,%0" +#ifndef CONFIG_LOCKMETER static inline int spin_trylock(spinlock_t *lock) +#else +static inline int nonmetered_spin_trylock(spinlock_t *lock) +#endif { char oldval; __asm__ __volatile__( @@ -75,6 +79,24 @@ return oldval > 0; } +#ifdef CONFIG_LOCKMETER +extern void _spin_lock_ (spinlock_t *lock_ptr); +extern void _spin_unlock_ (spinlock_t *lock_ptr); +extern int _spin_trylock_(spinlock_t *); + +#define spin_trylock(lock) _spin_trylock_((lock)) + +#define nonmetered_spin_lock(_lock) \ +__asm__ __volatile__( \ + spin_lock_string \ + :"=m" ((spinlock_t *)(_lock)->lock) : : "memory") + +#define nonmetered_spin_unlock(_lock) \ +__asm__ __volatile__( \ + spin_unlock_string \ + :"=m" ((spinlock_t *)(_lock)->lock) : : "memory") +#endif /* CONFIG_LOCKMETER */ + static inline void spin_lock(spinlock_t *lock) { #if SPINLOCK_DEBUG @@ -85,9 +107,13 @@ BUG(); } #endif +#ifdef CONFIG_LOCKMETER + _spin_lock_(lock); +#else __asm__ __volatile__( spin_lock_string :"=m" (lock->lock) : : "memory"); +#endif } static inline void spin_unlock(spinlock_t *lock) @@ -98,9 +124,13 @@ if (!spin_is_locked(lock)) BUG(); #endif +#ifdef CONFIG_LOCKMETER + _spin_unlock_(lock); +#else __asm__ __volatile__( spin_unlock_string :"=m" (lock->lock) : : "memory"); +#endif } /* @@ -143,13 +173,23 @@ */ /* the spinlock helpers are in arch/i386/kernel/semaphore.c */ + +#ifdef CONFIG_LOCKMETER +extern void _read_lock_ (rwlock_t *rw); +extern void _write_lock_ (rwlock_t *rw); +#endif + static inline void read_lock(rwlock_t *rw) { #if SPINLOCK_DEBUG if (rw->magic != RWLOCK_MAGIC) BUG(); #endif +#ifdef CONFIG_LOCKMETER + _read_lock_(rw); +#else __build_read_lock(rw, "__read_lock_failed"); +#endif } static inline void write_lock(rwlock_t *rw) @@ -158,13 +198,36 @@ if (rw->magic != RWLOCK_MAGIC) BUG(); #endif +#ifdef CONFIG_LOCKMETER + _write_lock_(rw); +#else __build_write_lock(rw, "__write_lock_failed"); +#endif } #define read_unlock(rw) asm volatile("lock ; incl %0" :"=m" ((rw)->lock) : : "memory") #define write_unlock(rw) asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ",%0":"=m" ((rw)->lock) : : "memory") +#ifdef CONFIG_LOCKMETER +static inline int nonmetered_read_trylock(rwlock_t *lock) +{ + atomic_t *count = (atomic_t *)lock; + atomic_dec(count); + if (count->counter > 0) + return 1; + atomic_inc(count); + return 0; +} +#endif + +#ifdef CONFIG_LOCKMETER +extern int _write_trylock_(rwlock_t *rw); +#define write_trylock(rw) _write_trylock_((rw)) + +static inline int nonmetered_write_trylock(rwlock_t *lock) +#else static inline int write_trylock(rwlock_t *lock) +#endif { atomic_t *count = (atomic_t *)lock; if (atomic_sub_and_test(RW_LOCK_BIAS, count)) diff -urN 2.4.0/include/asm-i386/system.h 2.4.0-ikd-1/include/asm-i386/system.h --- 2.4.0/include/asm-i386/system.h Fri Jan 5 18:48:41 2001 +++ 2.4.0-ikd-1/include/asm-i386/system.h Mon Jan 15 18:40:04 2001 @@ -9,9 +9,38 @@ #ifdef __KERNEL__ struct task_struct; /* one of the stranger aspects of C forward declarations.. */ + +#include +#ifndef CONFIG_KERNEL_DEBUGGING /* Fix the FASTCALL thing -Andrea */ extern void FASTCALL(__switch_to(struct task_struct *prev, struct task_struct *next)); +#else +extern void STDCALL(__switch_to(struct task_struct *prev, struct task_struct *next)); +#endif #define prepare_to_switch() do { } while(0) +#ifdef CONFIG_KERNEL_DEBUGGING /* we can' t use FASTCALL -Andrea */ +#define switch_to(prev,next,last) do { \ + asm volatile("pushl %%esi\n\t" \ + "pushl %%edi\n\t" \ + "pushl %%ebp\n\t" \ + "movl %%esp,%0\n\t" /* save ESP */ \ + "movl %3,%%esp\n\t" /* restore ESP */ \ + "movl $1f,%1\n\t" /* save EIP */ \ + "pushl %6\n\t" /* pass args throught the stack */ \ + "pushl %5\n\t" /* pass args throught the stack */ \ + "pushl %4\n\t" /* restore EIP */ \ + "jmp __switch_to\n" \ + "1:\t" \ + "popl %%ebp\n\t" \ + "popl %%edi\n\t" \ + "popl %%esi\n\t" \ + :"=m" (prev->thread.esp),"=m" (prev->thread.eip), \ + "=b" (last) \ + :"m" (next->thread.esp),"m" (next->thread.eip), \ + "a" (prev), "d" (next), \ + "b" (prev)); \ +} while (0) +#else /* original */ #define switch_to(prev,next,last) do { \ asm volatile("pushl %%esi\n\t" \ "pushl %%edi\n\t" \ @@ -31,6 +60,7 @@ "a" (prev), "d" (next), \ "b" (prev)); \ } while (0) +#endif #define _set_base(addr,base) do { unsigned long __pr; \ __asm__ __volatile__ ("movw %%dx,%1\n\t" \ diff -urN 2.4.0/include/asm-ia64/kdb.h 2.4.0-ikd-1/include/asm-ia64/kdb.h --- 2.4.0/include/asm-ia64/kdb.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-ia64/kdb.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,10 @@ +/* + * Dummy include/asm/kdb.h for ia64. + */ +#if !defined(_ASM_KDB_H) +#define _ASM_KDB_H +#define KDB_ENTER() +struct pt_regs; +typedef struct pt_regs *kdb_eframe_t; +typedef unsigned long kdb_machreg_t; +#endif /* ASM_KDB_H */ diff -urN 2.4.0/include/asm-ia64/profiler.h 2.4.0-ikd-1/include/asm-ia64/profiler.h --- 2.4.0/include/asm-ia64/profiler.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-ia64/profiler.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,12 @@ +#ifndef _LINUX_PROFILER_ASM_H +#define _LINUX_PROFILER_ASM_H + +#include + +#ifdef CONFIG_DEBUG_MCOUNT + +typedef unsigned long profiler_pc_t; + +#endif /* CONFIG_DEBUG_MCOUNT */ + +#endif /* _LINUX_PROFILER_ASM_H */ diff -urN 2.4.0/include/asm-m68k/kdb.h 2.4.0-ikd-1/include/asm-m68k/kdb.h --- 2.4.0/include/asm-m68k/kdb.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-m68k/kdb.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,10 @@ +/* + * Dummy include/asm/kdb.h for m68k. + */ +#if !defined(_ASM_KDB_H) +#define _ASM_KDB_H +#define KDB_ENTER() +struct pt_regs; +typedef struct pt_regs *kdb_eframe_t; +typedef unsigned long kdb_machreg_t; +#endif /* ASM_KDB_H */ diff -urN 2.4.0/include/asm-mips/kdb.h 2.4.0-ikd-1/include/asm-mips/kdb.h --- 2.4.0/include/asm-mips/kdb.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-mips/kdb.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,10 @@ +/* + * Dummy include/asm/kdb.h for mips. + */ +#if !defined(_ASM_KDB_H) +#define _ASM_KDB_H +#define KDB_ENTER() +struct pt_regs; +typedef struct pt_regs *kdb_eframe_t; +typedef unsigned long kdb_machreg_t; +#endif /* ASM_KDB_H */ diff -urN 2.4.0/include/asm-mips64/kdb.h 2.4.0-ikd-1/include/asm-mips64/kdb.h --- 2.4.0/include/asm-mips64/kdb.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-mips64/kdb.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,10 @@ +/* + * Dummy include/asm/kdb.h for mips64. + */ +#if !defined(_ASM_KDB_H) +#define _ASM_KDB_H +#define KDB_ENTER() +struct pt_regs; +typedef struct pt_regs *kdb_eframe_t; +typedef unsigned long kdb_machreg_t; +#endif /* ASM_KDB_H */ diff -urN 2.4.0/include/asm-ppc/kdb.h 2.4.0-ikd-1/include/asm-ppc/kdb.h --- 2.4.0/include/asm-ppc/kdb.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-ppc/kdb.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,10 @@ +/* + * Dummy include/asm/kdb.h for ppc. + */ +#if !defined(_ASM_KDB_H) +#define _ASM_KDB_H +#define KDB_ENTER() +struct pt_regs; +typedef struct pt_regs *kdb_eframe_t; +typedef unsigned long kdb_machreg_t; +#endif /* ASM_KDB_H */ diff -urN 2.4.0/include/asm-s390/kdb.h 2.4.0-ikd-1/include/asm-s390/kdb.h --- 2.4.0/include/asm-s390/kdb.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-s390/kdb.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,10 @@ +/* + * Dummy include/asm/kdb.h for s390. + */ +#if !defined(_ASM_KDB_H) +#define _ASM_KDB_H +#define KDB_ENTER() +struct pt_regs; +typedef struct pt_regs *kdb_eframe_t; +typedef unsigned long kdb_machreg_t; +#endif /* ASM_KDB_H */ diff -urN 2.4.0/include/asm-sh/kdb.h 2.4.0-ikd-1/include/asm-sh/kdb.h --- 2.4.0/include/asm-sh/kdb.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-sh/kdb.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,10 @@ +/* + * Dummy include/asm/kdb.h for sh. + */ +#if !defined(_ASM_KDB_H) +#define _ASM_KDB_H +#define KDB_ENTER() +struct pt_regs; +typedef struct pt_regs *kdb_eframe_t; +typedef unsigned long kdb_machreg_t; +#endif /* ASM_KDB_H */ diff -urN 2.4.0/include/asm-sparc/kdb.h 2.4.0-ikd-1/include/asm-sparc/kdb.h --- 2.4.0/include/asm-sparc/kdb.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-sparc/kdb.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,10 @@ +/* + * Dummy include/asm/kdb.h for sparc. + */ +#if !defined(_ASM_KDB_H) +#define _ASM_KDB_H +#define KDB_ENTER() +struct pt_regs; +typedef struct pt_regs *kdb_eframe_t; +typedef unsigned long kdb_machreg_t; +#endif /* ASM_KDB_H */ diff -urN 2.4.0/include/asm-sparc64/kdb.h 2.4.0-ikd-1/include/asm-sparc64/kdb.h --- 2.4.0/include/asm-sparc64/kdb.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/asm-sparc64/kdb.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,10 @@ +/* + * Dummy include/asm/kdb.h for sparc64. + */ +#if !defined(_ASM_KDB_H) +#define _ASM_KDB_H +#define KDB_ENTER() +struct pt_regs; +typedef struct pt_regs *kdb_eframe_t; +typedef unsigned long kdb_machreg_t; +#endif /* ASM_KDB_H */ diff -urN 2.4.0/include/linux/dis-asm.h 2.4.0-ikd-1/include/linux/dis-asm.h --- 2.4.0/include/linux/dis-asm.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/linux/dis-asm.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,304 @@ +/* Interface between the opcode library and its callers. + Written by Cygnus Support, 1993. + + The opcode library (libopcodes.a) provides instruction decoders for + a large variety of instruction sets, callable with an identical + interface, for making instruction-processing programs more independent + of the instruction set being processed. */ + +/* Hacked by Scott Lurndal at SGI (02/1999) for linux kernel debugger */ +/* Upgraded to cygnus CVS Keith Owens 30 Oct 2000 */ + +#ifndef DIS_ASM_H +#define DIS_ASM_H + +#ifdef __cplusplus +extern "C" { +#endif + + /* + * Misc definitions + */ +#define PARAMS(x) x +#define PTR void * +#define FILE int +#if !defined(NULL) +#define NULL 0 +#endif + +#define abort() dis_abort(__LINE__) + +extern inline void +dis_abort(int line) +{ + panic("Aborting dissembler in %s @ line %d\n", __FILE__, line); +} + +#include +#include +#define xstrdup(string) ({ char *res = kstrdup(string, GFP_KERNEL); if (!res) BUG(); res; }) +#define xmalloc(size) ({ void *res = kmalloc(size, GFP_KERNEL); if (!res) BUG(); res; }) +#define free(address) kfree(address) + +#include + +typedef int (*fprintf_ftype) PARAMS((PTR, const char*, ...)); + +enum dis_insn_type { + dis_noninsn, /* Not a valid instruction */ + dis_nonbranch, /* Not a branch instruction */ + dis_branch, /* Unconditional branch */ + dis_condbranch, /* Conditional branch */ + dis_jsr, /* Jump to subroutine */ + dis_condjsr, /* Conditional jump to subroutine */ + dis_dref, /* Data reference instruction */ + dis_dref2 /* Two data references in instruction */ +}; + +/* This struct is passed into the instruction decoding routine, + and is passed back out into each callback. The various fields are used + for conveying information from your main routine into your callbacks, + for passing information into the instruction decoders (such as the + addresses of the callback functions), or for passing information + back from the instruction decoders to their callers. + + It must be initialized before it is first passed; this can be done + by hand, or using one of the initialization macros below. */ + +typedef struct disassemble_info { + fprintf_ftype fprintf_func; + PTR stream; + PTR application_data; + + /* Target description. We could replace this with a pointer to the bfd, + but that would require one. There currently isn't any such requirement + so to avoid introducing one we record these explicitly. */ + /* The bfd_flavour. This can be bfd_target_unknown_flavour. */ + enum bfd_flavour flavour; + /* The bfd_arch value. */ + enum bfd_architecture arch; + /* The bfd_mach value. */ + unsigned long mach; + /* Endianness (for bi-endian cpus). Mono-endian cpus can ignore this. */ + enum bfd_endian endian; + + /* An array of pointers to symbols either at the location being disassembled + or at the start of the function being disassembled. The array is sorted + so that the first symbol is intended to be the one used. The others are + present for any misc. purposes. This is not set reliably, but if it is + not NULL, it is correct. */ + asymbol **symbols; + /* Number of symbols in array. */ + int num_symbols; + + /* For use by the disassembler. + The top 16 bits are reserved for public use (and are documented here). + The bottom 16 bits are for the internal use of the disassembler. */ + unsigned long flags; +#define INSN_HAS_RELOC 0x80000000 + PTR private_data; + + /* Function used to get bytes to disassemble. MEMADDR is the + address of the stuff to be disassembled, MYADDR is the address to + put the bytes in, and LENGTH is the number of bytes to read. + INFO is a pointer to this struct. + Returns an errno value or 0 for success. */ + int (*read_memory_func) + PARAMS ((bfd_vma memaddr, bfd_byte *myaddr, unsigned int length, + struct disassemble_info *info)); + + /* Function which should be called if we get an error that we can't + recover from. STATUS is the errno value from read_memory_func and + MEMADDR is the address that we were trying to read. INFO is a + pointer to this struct. */ + void (*memory_error_func) + PARAMS ((int status, bfd_vma memaddr, struct disassemble_info *info)); + + /* Function called to print ADDR. */ + void (*print_address_func) + PARAMS ((bfd_vma addr, struct disassemble_info *info)); + + /* Function called to determine if there is a symbol at the given ADDR. + If there is, the function returns 1, otherwise it returns 0. + This is used by ports which support an overlay manager where + the overlay number is held in the top part of an address. In + some circumstances we want to include the overlay number in the + address, (normally because there is a symbol associated with + that address), but sometimes we want to mask out the overlay bits. */ + int (* symbol_at_address_func) + PARAMS ((bfd_vma addr, struct disassemble_info * info)); + + /* These are for buffer_read_memory. */ + bfd_byte *buffer; + bfd_vma buffer_vma; + unsigned int buffer_length; + + /* This variable may be set by the instruction decoder. It suggests + the number of bytes objdump should display on a single line. If + the instruction decoder sets this, it should always set it to + the same value in order to get reasonable looking output. */ + int bytes_per_line; + + /* the next two variables control the way objdump displays the raw data */ + /* For example, if bytes_per_line is 8 and bytes_per_chunk is 4, the */ + /* output will look like this: + 00: 00000000 00000000 + with the chunks displayed according to "display_endian". */ + int bytes_per_chunk; + enum bfd_endian display_endian; + + /* Number of octets per incremented target address + Normally one, but some DSPs have byte sizes of 16 or 32 bits + */ + unsigned int octets_per_byte; + + /* Results from instruction decoders. Not all decoders yet support + this information. This info is set each time an instruction is + decoded, and is only valid for the last such instruction. + + To determine whether this decoder supports this information, set + insn_info_valid to 0, decode an instruction, then check it. */ + + char insn_info_valid; /* Branch info has been set. */ + char branch_delay_insns; /* How many sequential insn's will run before + a branch takes effect. (0 = normal) */ + char data_size; /* Size of data reference in insn, in bytes */ + enum dis_insn_type insn_type; /* Type of instruction */ + bfd_vma target; /* Target address of branch or dref, if known; + zero if unknown. */ + bfd_vma target2; /* Second target address for dref2 */ + + /* Command line options specific to the target disassembler. */ + char * disassembler_options; + +} disassemble_info; + + +/* Standard disassemblers. Disassemble one instruction at the given + target address. Return number of bytes processed. */ +typedef int (*disassembler_ftype) + PARAMS((bfd_vma, disassemble_info *)); + +extern int print_insn_big_mips PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_little_mips PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_i386_att PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_i386_intel PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_ia64 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_i370 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_m68hc11 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_m68hc12 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_m68k PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_z8001 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_z8002 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_h8300 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_h8300h PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_h8300s PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_h8500 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_alpha PARAMS ((bfd_vma, disassemble_info*)); +extern disassembler_ftype arc_get_disassembler PARAMS ((int, int)); +extern int print_insn_big_arm PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_little_arm PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_sparc PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_big_a29k PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_little_a29k PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_i860 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_i960 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_sh PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_shl PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_hppa PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_fr30 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_m32r PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_m88k PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_mcore PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_mn10200 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_mn10300 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_ns32k PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_big_powerpc PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_little_powerpc PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_rs6000 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_w65 PARAMS ((bfd_vma, disassemble_info*)); +extern disassembler_ftype cris_get_disassembler PARAMS ((bfd *)); +extern int print_insn_d10v PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_d30v PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_v850 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_tic30 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_vax PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_tic54x PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_tic80 PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_pj PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_avr PARAMS ((bfd_vma, disassemble_info*)); + +extern void print_arm_disassembler_options PARAMS ((FILE *)); +extern void parse_arm_disassembler_option PARAMS ((char *)); +extern int get_arm_regname_num_options PARAMS ((void)); +extern int set_arm_regname_option PARAMS ((int)); +extern int get_arm_regnames PARAMS ((int, const char **, const char **, const char ***)); + +/* Fetch the disassembler for a given BFD, if that support is available. */ +extern disassembler_ftype disassembler PARAMS ((bfd *)); + +/* Document any target specific options available from the disassembler. */ +extern void disassembler_usage PARAMS ((FILE *)); + + +/* This block of definitions is for particular callers who read instructions + into a buffer before calling the instruction decoder. */ + +/* Here is a function which callers may wish to use for read_memory_func. + It gets bytes from a buffer. */ +extern int buffer_read_memory + PARAMS ((bfd_vma, bfd_byte *, unsigned int, struct disassemble_info *)); + +/* This function goes with buffer_read_memory. + It prints a message using info->fprintf_func and info->stream. */ +extern void perror_memory PARAMS ((int, bfd_vma, struct disassemble_info *)); + + +/* Just print the address in hex. This is included for completeness even + though both GDB and objdump provide their own (to print symbolic + addresses). */ +extern void generic_print_address + PARAMS ((bfd_vma, struct disassemble_info *)); + +/* Always true. */ +extern int generic_symbol_at_address + PARAMS ((bfd_vma, struct disassemble_info *)); + +/* Macro to initialize a disassemble_info struct. This should be called + by all applications creating such a struct. */ +#define INIT_DISASSEMBLE_INFO(INFO, STREAM, FPRINTF_FUNC) \ + (INFO).flavour = bfd_target_unknown_flavour, \ + (INFO).arch = bfd_arch_unknown, \ + (INFO).mach = 0, \ + (INFO).endian = BFD_ENDIAN_UNKNOWN, \ + (INFO).octets_per_byte = 1, \ + INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) + +/* Call this macro to initialize only the internal variables for the + disassembler. Architecture dependent things such as byte order, or machine + variant are not touched by this macro. This makes things much easier for + GDB which must initialize these things separately. */ + +#define INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) \ + (INFO).fprintf_func = (fprintf_ftype)(FPRINTF_FUNC), \ + (INFO).stream = (PTR)(STREAM), \ + (INFO).symbols = NULL, \ + (INFO).num_symbols = 0, \ + (INFO).buffer = NULL, \ + (INFO).buffer_vma = 0, \ + (INFO).buffer_length = 0, \ + (INFO).read_memory_func = buffer_read_memory, \ + (INFO).memory_error_func = perror_memory, \ + (INFO).print_address_func = generic_print_address, \ + (INFO).symbol_at_address_func = generic_symbol_at_address, \ + (INFO).flags = 0, \ + (INFO).bytes_per_line = 0, \ + (INFO).bytes_per_chunk = 0, \ + (INFO).display_endian = BFD_ENDIAN_UNKNOWN, \ + (INFO).insn_info_valid = 0 + +#ifdef __cplusplus +}; +#endif + +#endif /* ! defined (DIS_ASM_H) */ diff -urN 2.4.0/include/linux/elf.h 2.4.0-ikd-1/include/linux/elf.h --- 2.4.0/include/linux/elf.h Mon Jan 15 01:57:26 2001 +++ 2.4.0-ikd-1/include/linux/elf.h Mon Jan 15 18:40:04 2001 @@ -16,8 +16,10 @@ typedef __u16 Elf64_Half; typedef __s16 Elf64_SHalf; typedef __u64 Elf64_Off; -typedef __s64 Elf64_Sword; -typedef __u64 Elf64_Word; +typedef __s32 Elf64_Sword; +typedef __u32 Elf64_Word; +typedef __u64 Elf64_Xword; +typedef __s64 Elf64_Sxword; /* These constants are for the segment types stored in the image headers */ #define PT_NULL 0 @@ -176,10 +178,10 @@ } Elf32_Dyn; typedef struct { - Elf64_Word d_tag; /* entry tag value */ + Elf64_Sxword d_tag; /* entry tag value */ union { - Elf64_Word d_val; - Elf64_Word d_ptr; + Elf64_Xword d_val; + Elf64_Addr d_ptr; } d_un; } Elf64_Dyn; @@ -370,7 +372,7 @@ typedef struct elf64_rel { Elf64_Addr r_offset; /* Location at which to apply the action */ - Elf64_Word r_info; /* index and type of relocation */ + Elf64_Xword r_info; /* index and type of relocation */ } Elf64_Rel; typedef struct elf32_rela{ @@ -381,8 +383,8 @@ typedef struct elf64_rela { Elf64_Addr r_offset; /* Location at which to apply the action */ - Elf64_Word r_info; /* index and type of relocation */ - Elf64_Word r_addend; /* Constant addend used to compute value */ + Elf64_Xword r_info; /* index and type of relocation */ + Elf64_Sxword r_addend; /* Constant addend used to compute value */ } Elf64_Rela; typedef struct elf32_sym{ @@ -395,12 +397,12 @@ } Elf32_Sym; typedef struct elf64_sym { - Elf32_Word st_name; /* Symbol name, index in string tbl (yes, Elf32) */ + Elf64_Word st_name; /* Symbol name, index in string tbl */ unsigned char st_info; /* Type and binding attributes */ unsigned char st_other; /* No defined meaning, 0 */ Elf64_Half st_shndx; /* Associated section index */ Elf64_Addr st_value; /* Value of the symbol */ - Elf64_Word st_size; /* Associated symbol size */ + Elf64_Xword st_size; /* Associated symbol size */ } Elf64_Sym; @@ -425,19 +427,19 @@ typedef struct elf64_hdr { unsigned char e_ident[16]; /* ELF "magic number" */ - Elf64_SHalf e_type; + Elf64_Half e_type; Elf64_Half e_machine; - __s32 e_version; + Elf64_Word e_version; Elf64_Addr e_entry; /* Entry point virtual address */ Elf64_Off e_phoff; /* Program header table file offset */ Elf64_Off e_shoff; /* Section header table file offset */ - __s32 e_flags; - Elf64_SHalf e_ehsize; - Elf64_SHalf e_phentsize; - Elf64_SHalf e_phnum; - Elf64_SHalf e_shentsize; - Elf64_SHalf e_shnum; - Elf64_SHalf e_shstrndx; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; } Elf64_Ehdr; /* These constants define the permissions on sections in the program @@ -458,14 +460,14 @@ } Elf32_Phdr; typedef struct elf64_phdr { - __s32 p_type; - __s32 p_flags; + Elf64_Word p_type; + Elf64_Word p_flags; Elf64_Off p_offset; /* Segment file offset */ Elf64_Addr p_vaddr; /* Segment virtual address */ Elf64_Addr p_paddr; /* Segment physical address */ - Elf64_Word p_filesz; /* Segment size in file */ - Elf64_Word p_memsz; /* Segment size in memory */ - Elf64_Word p_align; /* Segment alignment, file & memory */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment, file & memory */ } Elf64_Phdr; /* sh_type */ @@ -522,16 +524,16 @@ } Elf32_Shdr; typedef struct elf64_shdr { - Elf32_Word sh_name; /* Section name, index in string tbl (yes Elf32) */ - Elf32_Word sh_type; /* Type of section (yes Elf32) */ - Elf64_Word sh_flags; /* Miscellaneous section attributes */ + Elf64_Word sh_name; /* Section name, index in string tbl */ + Elf64_Word sh_type; /* Type of section */ + Elf64_Xword sh_flags; /* Miscellaneous section attributes */ Elf64_Addr sh_addr; /* Section virtual addr at execution */ Elf64_Off sh_offset; /* Section file offset */ - Elf64_Word sh_size; /* Size of section in bytes */ - Elf32_Word sh_link; /* Index of another section (yes Elf32) */ - Elf32_Word sh_info; /* Additional section information (yes Elf32) */ - Elf64_Word sh_addralign; /* Section alignment */ - Elf64_Word sh_entsize; /* Entry size if section holds table */ + Elf64_Xword sh_size; /* Size of section in bytes */ + Elf64_Word sh_link; /* Index of another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ } Elf64_Shdr; #define EI_MAG0 0 /* e_ident[] indexes */ @@ -578,15 +580,10 @@ } Elf32_Nhdr; /* Note header in a PT_NOTE section */ -/* - * For now we use the 32 bit version of the structure until we figure - * out whether we need anything better. Note - on the Alpha, "unsigned int" - * is only 32 bits. - */ typedef struct elf64_note { - Elf32_Word n_namesz; /* Name size */ - Elf32_Word n_descsz; /* Content size */ - Elf32_Word n_type; /* Content type */ + Elf64_Word n_namesz; /* Name size */ + Elf64_Word n_descsz; /* Content size */ + Elf64_Word n_type; /* Content type */ } Elf64_Nhdr; #if ELF_CLASS == ELFCLASS32 diff -urN 2.4.0/include/linux/kallsyms.h 2.4.0-ikd-1/include/linux/kallsyms.h --- 2.4.0/include/linux/kallsyms.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/linux/kallsyms.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,141 @@ +/* kallsyms headers + Copyright 2000 Keith Owens + + This file is part of the Linux modutils. It is exported to kernel + space so debuggers can access the kallsyms data. + + The kallsyms data contains all the non-stack symbols from a kernel + or a module. The kernel symbols are held between __start___kallsyms + and __stop___kallsyms. The symbols for a module are accessed via + the struct module chain which is based at module_list. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ident "$Id$" + +#ifndef MODUTILS_KALLSYMS_H +#define MODUTILS_KALLSYMS_H 1 + +/* Have to (re)define these ElfW entries here because external kallsyms + * code does not have access to modutils/include/obj.h. This code is + * included from user spaces tools (modutils) and kernel, they need + * different includes. + */ + +#ifndef ELFCLASS32 +#ifdef __KERNEL__ +#include +#else /* __KERNEL__ */ +#include +#endif /* __KERNEL__ */ +#endif /* ELFCLASS32 */ + +#ifndef ELFCLASSM +#define ELFCLASSM ELF_CLASS +#endif + +#ifndef ElfW +# if ELFCLASSM == ELFCLASS32 +# define ElfW(x) Elf32_ ## x +# define ELFW(x) ELF32_ ## x +# else +# define ElfW(x) Elf64_ ## x +# define ELFW(x) ELF64_ ## x +# endif +#endif + +/* Format of data in the kallsyms section. + * Most of the fields are small numbers but the total size and all + * offsets can be large so use the 32/64 bit types for these fields. + * + * Do not use sizeof() on these structures, modutils may be using extra + * fields. Instead use the size fields in the header to access the + * other bits of data. + */ + +struct kallsyms_header { + int size; /* Size of this header */ + ElfW(Word) total_size; /* Total size of kallsyms data */ + int sections; /* Number of section entries */ + ElfW(Off) section_off; /* Offset to first section entry */ + int section_size; /* Size of one section entry */ + int symbols; /* Number of symbol entries */ + ElfW(Off) symbol_off; /* Offset to first symbol entry */ + int symbol_size; /* Size of one symbol entry */ + ElfW(Off) string_off; /* Offset to first string */ + ElfW(Addr) start; /* Start address of first section */ + ElfW(Addr) end; /* End address of last section */ +}; + +struct kallsyms_section { + ElfW(Addr) start; /* Start address of section */ + ElfW(Word) size; /* Size of this section */ + ElfW(Off) name_off; /* Offset to section name */ + ElfW(Word) flags; /* Flags from section */ +}; + +struct kallsyms_symbol { + ElfW(Off) section_off; /* Offset to section that owns this symbol */ + ElfW(Addr) symbol_addr; /* Address of symbol */ + ElfW(Off) name_off; /* Offset to symbol name */ +}; + +#define KALLSYMS_SEC_NAME "__kallsyms" +#define KALLSYMS_IDX 2 /* obj_kallsyms creates kallsyms as section 2 */ + +#define kallsyms_next_sec(h,s) \ + ((s) = (struct kallsyms_section *)((char *)(s) + (h)->section_size)) +#define kallsyms_next_sym(h,s) \ + ((s) = (struct kallsyms_symbol *)((char *)(s) + (h)->symbol_size)) + +int kallsyms_symbol_to_address( + const char *name, /* Name to lookup */ + unsigned long *token, /* Which module to start with */ + const char **mod_name, /* Set to module name or "kernel" */ + unsigned long *mod_start, /* Set to start address of module */ + unsigned long *mod_end, /* Set to end address of module */ + const char **sec_name, /* Set to section name */ + unsigned long *sec_start, /* Set to start address of section */ + unsigned long *sec_end, /* Set to end address of section */ + const char **sym_name, /* Set to full symbol name */ + unsigned long *sym_start, /* Set to start address of symbol */ + unsigned long *sym_end /* Set to end address of symbol */ + ); + +int kallsyms_address_to_symbol( + unsigned long address, /* Address to lookup */ + const char **mod_name, /* Set to module name */ + unsigned long *mod_start, /* Set to start address of module */ + unsigned long *mod_end, /* Set to end address of module */ + const char **sec_name, /* Set to section name */ + unsigned long *sec_start, /* Set to start address of section */ + unsigned long *sec_end, /* Set to end address of section */ + const char **sym_name, /* Set to full symbol name */ + unsigned long *sym_start, /* Set to start address of symbol */ + unsigned long *sym_end /* Set to end address of symbol */ + ); + +int kallsyms_sections(void *token, + int (*callback)(void *, /* token */ + const char *, /* module name */ + const char *, /* section name */ + ElfW(Addr), /* Section start */ + ElfW(Addr), /* Section end */ + ElfW(Word) /* Section flags */ + ) + ); + +#endif /* kallsyms.h */ diff -urN 2.4.0/include/linux/kdb.h 2.4.0-ikd-1/include/linux/kdb.h --- 2.4.0/include/linux/kdb.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/linux/kdb.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,241 @@ +/* + * Minimalist Kernel Debugger + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * Copyright (C) 2000 Stephane Eranian + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + * Stephane Eranian 2000/06/05 + * move to v1.2 + * Keith Owens 2000/09/16 + * KDB v1.4 + * kdb=on/off/early at boot, /proc/sys/kernel/kdb. + * Env BTAPROMPT. + */ + + +#if !defined(__KDB_H) +#define __KDB_H + +#include +#include + +#define KDB_MAJOR_VERSION 1 +#define KDB_MINOR_VERSION 7 +#define KDB_TEST_VERSION "" + + /* + * kdb_initial_cpu is initialized to -1, and is set to the cpu + * number whenever the kernel debugger is entered. + */ +extern volatile int kdb_initial_cpu; +#ifdef CONFIG_KDB +#define KDB_IS_RUNNING() (kdb_initial_cpu != -1) +#else +#define KDB_IS_RUNNING() (0) +#endif /* CONFIG_KDB */ + + /* + * kdb_on + * + * Defines whether kdb is on or not. Default value + * is set by CONFIG_KDB_OFF. Boot with kdb=on/off + * or echo "[01]" > /proc/sys/kernel/kdb to change it. + */ +extern int kdb_on; + + /* + * kdb_port is initialized to zero, and is set to the I/O port + * address of the serial port when the console is setup in + * serial_console_setup. + */ +extern int kdb_port; + + /* + * KDB_FLAG_EARLYKDB is set when the 'kdb' option is specified + * as a boot parameter (e.g. via lilo). It indicates that the + * kernel debugger should be entered as soon as practical. + */ +#define KDB_FLAG_EARLYKDB 0x00000001 + + /* + * Internal debug flags + */ +#define KDB_DEBUG_FLAG_BT 0x0001 /* Stack traceback debug */ +#define KDB_DEBUG_FLAG_BP 0x0002 /* Breakpoint subsystem debug */ +#define KDB_DEBUG_FLAG_LBR 0x0004 /* Print last branch register */ +#define KDB_DEBUG_FLAG_AR 0x0008 /* Activation record, generic */ +#define KDB_DEBUG_FLAG_ARA 0x0010 /* Activation record, arch specific */ +#define KDB_DEBUG_FLAG_CALLBACK 0x0020 /* Event callbacks to kdb */ +#define KDB_DEBUG_FLAG_STATE 0x0040 /* State flags */ +#define KDB_DEBUG_FLAG_MASK 0xffff /* All debug flags */ +#define KDB_DEBUG_FLAG_SHIFT 16 /* Shift factor for dbflags */ + +extern volatile int kdb_flags; /* Global flags, see kdb_state for per cpu state */ + +#define KDB_FLAG(flag) (kdb_flags & KDB_FLAG_##flag) +#define KDB_FLAG_SET(flag) ((void)(kdb_flags |= KDB_FLAG_##flag)) +#define KDB_FLAG_CLEAR(flag) ((void)(kdb_flags &= ~KDB_FLAG_##flag)) +#define KDB_DEBUG(flag) (kdb_flags & (KDB_DEBUG_FLAG_##flag << KDB_DEBUG_FLAG_SHIFT)) +#define KDB_DEBUG_STATE(text,value) if (KDB_DEBUG(STATE)) kdb_print_state(text, value) + + /* + * Per cpu kdb state. A cpu can be under kdb control but outside kdb, + * for example when doing single step. + */ +volatile extern int kdb_state[ /*NR_CPUS*/ ]; +#define KDB_STATE_KDB 0x00000001 /* Cpu is inside kdb */ +#define KDB_STATE_LEAVING 0x00000002 /* Cpu is leaving kdb */ +#define KDB_STATE_CMD 0x00000004 /* Running a kdb command */ +#define KDB_STATE_KDB_CONTROL 0x00000008 /* This cpu is under kdb control */ +#define KDB_STATE_HOLD_CPU 0x00000010 /* Hold this cpu inside kdb */ +#define KDB_STATE_DOING_SS 0x00000020 /* Doing ss command */ +#define KDB_STATE_DOING_SSB 0x00000040 /* Doing ssb command, DOING_SS is also set */ +#define KDB_STATE_SSBPT 0x00000080 /* Install breakpoint after one ss, independent of DOING_SS */ +#define KDB_STATE_REENTRY 0x00000100 /* Valid re-entry into kdb */ +#define KDB_STATE_SUPPRESS 0x00000200 /* Suppress error messages */ +#define KDB_STATE_LONGJMP 0x00000400 /* longjmp() data is available */ +#define KDB_STATE_NO_WATCHDOG 0x00000800 /* Ignore watchdog */ +#define KDB_STATE_PRINTF_LOCK 0x00001000 /* Holds kdb_printf lock */ +#define KDB_STATE_WAIT_IPI 0x00002000 /* Waiting for kdb_ipi() NMI */ +#define KDB_STATE_RECURSE 0x00004000 /* Recursive entry to kdb */ +#define KDB_STATE_IP_ADJUSTED 0x00008000 /* Restart IP has been adjusted */ +#define KDB_STATE_ARCH 0xff000000 /* Reserved for arch specific use */ + +#define KDB_STATE_CPU(flag,cpu) (kdb_state[cpu] & KDB_STATE_##flag) +#define KDB_STATE_SET_CPU(flag,cpu) ((void)(kdb_state[cpu] |= KDB_STATE_##flag)) +#define KDB_STATE_CLEAR_CPU(flag,cpu) ((void)(kdb_state[cpu] &= ~KDB_STATE_##flag)) + +#define KDB_STATE(flag) KDB_STATE_CPU(flag,smp_processor_id()) +#define KDB_STATE_SET(flag) KDB_STATE_SET_CPU(flag,smp_processor_id()) +#define KDB_STATE_CLEAR(flag) KDB_STATE_CLEAR_CPU(flag,smp_processor_id()) + + /* + * External entry point for the kernel debugger. The pt_regs + * at the time of entry are supplied along with the reason for + * entry to the kernel debugger. + */ + +typedef enum { + KDB_REASON_CALL = 1, /* Call kdb() directly - regs invalid */ + KDB_REASON_FAULT, /* Kernel fault - regs valid */ + KDB_REASON_BREAK, /* Breakpoint inst. - regs valid */ + KDB_REASON_DEBUG, /* Debug Fault - regs valid */ + KDB_REASON_PANIC, /* Kernel Panic - regs valid */ + KDB_REASON_SWITCH, /* CPU switch - regs valid*/ + KDB_REASON_ENTER, /* KDB_ENTER() trap/fault - regs valid */ + KDB_REASON_KEYBOARD, /* Keyboard entry - regs valid */ + KDB_REASON_NMI, /* Non-maskable interrupt; regs valid */ + KDB_REASON_WATCHDOG, /* Watchdog interrupt; regs valid */ + KDB_REASON_FLTDBG, /* See below comment */ + KDB_REASON_RECURSE, /* Recursive entry to kdb; regs probably valid */ + KDB_REASON_SILENT, /* Silent entry/exit to kdb; regs invalid */ +} kdb_reason_t; + +/* KDB_REASON_FLTDBG: + * In some archs, we want to handle fault as debug itself but still + * be able to know that we came into kdb() from a fault handler rather + * than a break handler or keyboard. Since the actual FAULT handling + * may be diferent other wise, this code means that we came in from + * the fault handler, but treat it like DEBUG. + */ + + +#ifdef CONFIG_KDB +extern int kdb(kdb_reason_t, int, kdb_eframe_t); +#else +#define kdb(reason,error_code,frame) (0) +#endif + +typedef int (*kdb_func_t)(int, const char **, const char **, kdb_eframe_t); + + /* + * Symbol table format returned by kallsyms. + */ + +typedef struct __ksymtab { + unsigned long value; /* Address of symbol */ + const char *mod_name; /* Module containing symbol or "kernel" */ + unsigned long mod_start; + unsigned long mod_end; + const char *sec_name; /* Section containing symbol */ + unsigned long sec_start; + unsigned long sec_end; + const char *sym_name; /* Full symbol name, including any version */ + unsigned long sym_start; + unsigned long sym_end; + } kdb_symtab_t; + + /* + * Exported Symbols for kernel loadable modules to use. + */ +extern int kdb_register(char *, kdb_func_t, char *, char *, short); +extern int kdb_unregister(char *); + +extern unsigned long kdba_getword(unsigned long, size_t); +extern unsigned long kdba_putword(unsigned long, size_t, unsigned long); + +extern int kdbgetularg(const char *, unsigned long *); +extern char *kdbgetenv(const char *); +extern int kdbgetintenv(const char *, int *); +extern int kdbgetaddrarg(int, const char**, int*, unsigned long *, + long *, char **, kdb_eframe_t); +extern int kdbgetsymval(const char *, kdb_symtab_t *); +extern int kdbnearsym(unsigned long, kdb_symtab_t *); +extern void kdb_printf(const char *,...) + __attribute__ ((format (printf, 1, 2))); +extern void kdb_init(void); +extern void kdb_symbol_print(kdb_machreg_t, const kdb_symtab_t *, unsigned int); + +#if defined(CONFIG_SMP) + /* + * Kernel debugger non-maskable IPI handler. + */ +extern int kdb_ipi(kdb_eframe_t, void (*ack_interrupt)(void)); +extern void smp_kdb_stop(void); +#else /* CONFIG_SMP */ +#define smp_kdb_stop() +#endif /* CONFIG_SMP */ + + /* + * Interface from general kernel to enable any hardware + * error reporting mechanisms. Such as the Intel Machine + * Check Architecture, for example. + */ +extern void kdb_enablehwfault(void); + + /* + * Interface from kernel trap handling code to kernel debugger. + */ +extern int kdba_callback_die(struct pt_regs *, int, long, void*); +extern int kdba_callback_bp(struct pt_regs *, int, long, void*); +extern int kdba_callback_debug(struct pt_regs *, int, long, void *); + + /* + * Determine if a kernel address is valid or not. + */ + +extern int kdb_vmlist_check(unsigned long, unsigned long); + + /* + * Routine for debugging the debugger state. + */ + +extern void kdb_print_state(const char *, int); + +#endif /* __KDB_H */ diff -urN 2.4.0/include/linux/kdbprivate.h 2.4.0-ikd-1/include/linux/kdbprivate.h --- 2.4.0/include/linux/kdbprivate.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/linux/kdbprivate.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,318 @@ +/* + * Minimalist Kernel Debugger + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ +#if !defined(_KDBPRIVATE_H) +#define _KDBPRIVATE_H + +#include +#include + +#include + +/* + * Kernel Debugger Error codes. Must not overlap with command codes. + */ + +#define KDB_NOTFOUND -1 +#define KDB_ARGCOUNT -2 +#define KDB_BADWIDTH -3 +#define KDB_BADRADIX -4 +#define KDB_NOTENV -5 +#define KDB_NOENVVALUE -6 +#define KDB_NOTIMP -7 +#define KDB_ENVFULL -8 +#define KDB_ENVBUFFULL -9 +#define KDB_TOOMANYBPT -10 +#define KDB_TOOMANYDBREGS -11 +#define KDB_DUPBPT -12 +#define KDB_BPTNOTFOUND -13 +#define KDB_BADMODE -14 +#define KDB_BADINT -15 +#define KDB_INVADDRFMT -16 +#define KDB_BADREG -17 +#define KDB_BADCPUNUM -18 +#define KDB_BADLENGTH -19 +#define KDB_NOBP -20 + +/* + * Kernel Debugger Command codes. Must not overlap with error codes. + */ +#define KDB_CMD_GO -1001 +#define KDB_CMD_CPU -1002 +#define KDB_CMD_SS -1003 +#define KDB_CMD_SSB -1004 + + /* + * kdb_nextline + * + * Contains the current line number on the screen. Used + * to handle the built-in pager (LINES env variable) + */ +extern int kdb_nextline; + + /* + * kdb_diemsg + * + * Contains a pointer to the last string supplied to the + * kernel 'die' panic function. + */ +extern char *kdb_diemsg; + + /* + * Breakpoint state + * + * Each active and inactive breakpoint is represented by + * an instance of the following data structure. + */ + +typedef struct _kdb_bp { + bfd_vma bp_addr; /* Address breakpoint is present at */ + kdb_machinst_t bp_inst; /* Replaced instruction */ + + unsigned int bp_free:1; /* This entry is available */ + + unsigned int bp_enabled:1; /* Breakpoint is active in register */ + unsigned int bp_global:1; /* Global to all processors */ + + unsigned int bp_hardtype:1; /* Uses hardware register */ + unsigned int bp_forcehw:1; /* Force hardware register */ + unsigned int bp_instvalid:1; /* 0=bp_inst invalid, 1=bp_inst valid */ + unsigned int bp_installed:1; /* Breakpoint is installed */ + unsigned int bp_delay:1; /* Do delayed bp handling */ + unsigned int bp_delayed:1; /* Delayed breakpoint */ + + int bp_cpu; /* Cpu # (if bp_global == 0) */ + kdbhard_bp_t bp_template; /* Hardware breakpoint template */ + kdbhard_bp_t *bp_hard; /* Hardware breakpoint structure */ + int bp_adjust; /* Adjustment to PC for real instruction */ +} kdb_bp_t; + + /* + * Breakpoint handling subsystem global variables + */ +extern kdb_bp_t kdb_breakpoints[/* KDB_MAXBPT */]; + + /* + * Breakpoint architecture dependent functions. Must be provided + * in some form for all architectures. + */ +extern void kdba_initbp(void); +extern void kdba_printbp(kdb_bp_t *); +extern void kdba_printbpreg(kdbhard_bp_t *); +extern kdbhard_bp_t *kdba_allocbp(kdbhard_bp_t *, int *); +extern void kdba_freebp(kdbhard_bp_t *); +extern int kdba_parsebp(int, const char**, int *, kdb_bp_t*); +extern char *kdba_bptype(kdbhard_bp_t *); +extern void kdba_setsinglestep(kdb_eframe_t); +extern void kdba_clearsinglestep(kdb_eframe_t); + + /* + * Adjust instruction pointer architecture dependent function. Must be + * provided in some form for all architectures. + */ +extern void kdba_adjust_ip(kdb_reason_t, int, kdb_eframe_t); + + /* + * KDB-only global function prototypes. + */ +extern void kdb_id1(unsigned long); +extern void kdb_id_init(void); + + /* + * Architecture dependent function to enable any + * processor machine check exception handling modes. + */ +extern void kdba_enable_mce(void); + +extern void kdba_enable_lbr(void); +extern void kdba_disable_lbr(void); +extern void kdba_print_lbr(void); + + /* + * Initialization functions. + */ +extern void kdba_init(void); +extern void kdb_io_init(void); + + /* + * Architecture specific function to read a string. + */ +extern char * kdba_read(char *, size_t); + + /* + * Data for a single activation record on stack. + */ + +typedef struct __kdb_activation_record { + kdb_machreg_t start; /* -> start of activation record */ + kdb_machreg_t end; /* -> end+1 of activation record */ + kdb_machreg_t ret; /* Return address to caller */ + kdb_machreg_t oldfp; /* Frame pointer for caller's frame */ + kdb_machreg_t fp; /* Frame pointer for callee's frame */ + kdb_machreg_t arg0; /* -> First argument on stack (in previous ar) */ + int locals; /* Bytes allocated for local variables */ + int regs; /* Bytes allocated for saved registers */ + int args; /* Bytes allocated for arguments (in previous ar) */ + int setup; /* Bytes allocated for setup data */ +} kdb_ar_t; + + /* + * General Stack Traceback functions. + */ + +extern int kdb_get_next_ar(kdb_machreg_t, kdb_machreg_t, + kdb_machreg_t, kdb_machreg_t, + kdb_machreg_t, + kdb_ar_t *, kdb_symtab_t *); + + /* + * Architecture specific Stack Traceback functions. + */ + +struct task_struct; + +extern int kdba_bt_stack(struct pt_regs *, kdb_machreg_t *, + int, struct task_struct *); +extern int kdba_bt_process(struct task_struct *, int); +extern int kdba_prologue(const kdb_symtab_t *, kdb_machreg_t, + kdb_machreg_t, kdb_machreg_t, kdb_machreg_t, + int, kdb_ar_t *); + /* + * KDB Command Table + */ + +typedef struct _kdbtab { + char *cmd_name; /* Command name */ + kdb_func_t cmd_func; /* Function to execute command */ + char *cmd_usage; /* Usage String for this command */ + char *cmd_help; /* Help message for this command */ + short cmd_flags; /* Parsing flags */ + short cmd_minlen; /* Minimum legal # command chars required */ +} kdbtab_t; + + /* + * External command function declarations + */ + +extern int kdb_id(int, const char **, const char **, kdb_eframe_t); +extern int kdb_bp(int, const char **, const char **, kdb_eframe_t); +extern int kdb_bc(int, const char **, const char **, kdb_eframe_t); +extern int kdb_bt(int, const char **, const char **, kdb_eframe_t); +extern int kdb_ss(int, const char **, const char **, kdb_eframe_t); + + /* + * External utility function declarations + */ +extern char* kdb_getstr(char *, size_t, char *); + + /* + * Register contents manipulation + */ +extern int kdba_getregcontents(const char *, kdb_eframe_t, kdb_machreg_t *); +extern int kdba_setregcontents(const char *, kdb_eframe_t, kdb_machreg_t); +extern int kdba_dumpregs(struct pt_regs *, const char *, const char *); +extern int kdba_setpc(kdb_eframe_t, kdb_machreg_t); +extern kdb_machreg_t kdba_getpc(kdb_eframe_t); + + /* + * Debug register handling. + */ +extern void kdba_installdbreg(kdb_bp_t*); +extern void kdba_removedbreg(kdb_bp_t*); + + /* + * Breakpoint handling - External interfaces + */ +extern void kdb_initbptab(void); +extern void kdb_bp_install_global(kdb_eframe_t); +extern void kdb_bp_install_local(kdb_eframe_t); +extern void kdb_bp_remove_global(void); +extern void kdb_bp_remove_local(void); + + /* + * Breakpoint handling - Internal to kdb_bp.c/kdba_bp.c + */ +extern void kdba_installbp(kdb_eframe_t ef, kdb_bp_t *); +extern void kdba_removebp(kdb_bp_t *); + + +typedef enum { + KDB_DB_BPT, /* Breakpoint */ + KDB_DB_SS, /* Single-step trap */ + KDB_DB_SSB, /* Single step to branch */ + KDB_DB_SSBPT, /* Single step over breakpoint */ + KDB_DB_NOBPT /* Spurious breakpoint */ +} kdb_dbtrap_t; + +extern kdb_dbtrap_t kdba_db_trap(kdb_eframe_t); /* DEBUG trap/fault handler */ +extern kdb_dbtrap_t kdba_bp_trap(kdb_eframe_t); /* Breakpoint trap/fault hdlr */ + + /* + * Interrupt Handling + */ +typedef int kdb_intstate_t; + +extern void kdba_disableint(kdb_intstate_t *); +extern void kdba_restoreint(kdb_intstate_t *); + + /* + * SMP and process stack manipulation routines. + */ +extern int kdba_ipi(kdb_eframe_t, void (*)(void)); +extern int kdba_main_loop(kdb_reason_t, kdb_reason_t, int, kdb_dbtrap_t, kdb_eframe_t); +extern int kdb_main_loop(kdb_reason_t, kdb_reason_t, int, kdb_dbtrap_t, kdb_eframe_t); + + /* + * General Disassembler interfaces + */ +extern int kdb_dis_fprintf_dummy(PTR, const char *, ...) __attribute__ ((format (printf, 2, 3))); +extern disassemble_info kdb_di; + + /* + * Architecture Dependent Disassembler interfaces + */ +extern void kdba_printaddress(kdb_machreg_t, disassemble_info *, int); +extern int kdba_id_printinsn(kdb_machreg_t, disassemble_info *); +extern int kdba_id_parsemode(const char *, disassemble_info*); +extern void kdba_id_init(disassemble_info *); +extern void kdba_check_pc(kdb_machreg_t *); + + /* + * Miscellaneous functions and data areas + */ +extern int kdb_getcurrentframe(kdb_eframe_t); +extern void kdb_resetkeyboard(void); +extern char *kdb_cmds[]; + + /* + * Defines for kdb_symbol_print. + */ +#define KDB_SP_SPACEB 0x0001 /* Space before string */ +#define KDB_SP_SPACEA 0x0002 /* Space after string */ +#define KDB_SP_PAREN 0x0004 /* Parenthesis around string */ +#define KDB_SP_VALUE 0x0008 /* Print the value of the address */ +#define KDB_SP_SYMSIZE 0x0010 /* Print the size of the symbol */ +#define KDB_SP_NEWLINE 0x0020 /* Newline after string */ +#define KDB_SP_DEFAULT (KDB_SP_VALUE|KDB_SP_PAREN) + +#endif /* !_KDBPRIVATE_H */ + diff -urN 2.4.0/include/linux/kernel.h 2.4.0-ikd-1/include/linux/kernel.h --- 2.4.0/include/linux/kernel.h Thu Dec 14 22:34:14 2000 +++ 2.4.0-ikd-1/include/linux/kernel.h Mon Jan 15 18:40:04 2001 @@ -8,6 +8,7 @@ #ifdef __KERNEL__ #include +#include #include #include @@ -22,7 +23,11 @@ #define LONG_MIN (-LONG_MAX - 1) #define ULONG_MAX (~0UL) -#define STACK_MAGIC 0xdeadbeef +#if BITS_PER_LONG < 64 +# define STACK_MAGIC 0xdeadbeef +#else +# define STACK_MAGIC 0xfeedbabedeadbeef +#endif #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) @@ -39,10 +44,12 @@ # define ATTRIB_NORET __attribute__((noreturn)) # define NORET_AND noreturn, -#ifdef __i386__ +#if defined(__i386__) && !defined(CONFIG_KERNEL_DEBUGGING) #define FASTCALL(x) x __attribute__((regparm(3))) +#define STDCALL(x) x #else #define FASTCALL(x) x +#define STDCALL(x) __attribute__((stdcall)) x #endif struct semaphore; diff -urN 2.4.0/include/linux/lockmeter.h 2.4.0-ikd-1/include/linux/lockmeter.h --- 2.4.0/include/linux/lockmeter.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/linux/lockmeter.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,159 @@ +/* + * Copyright (C) 1999,2000 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.h by Jack Steiner (steiner@sgi.com) + * + * Modified by Ray Bryant (raybry@us.ibm.com) Feb-Mar 2000 + * Added cache of index in spinlock_t to reduce overhead + * of "hold" time reporting for spinlocks. + * Changes Copyright (C) 2000 IBM, Inc. + */ + +#ifndef _LINUX_LOCKMETER_H +#define _LINUX_LOCKMETER_H + +/* + * Values for the "action" parameter passed to lstat_update. + * ZZZ - do we want a try-success status here??? + */ +#define LSTAT_ACT_NO_WAIT 0 +#define LSTAT_ACT_SPIN 1 +#define LSTAT_ACT_SLEPT 2 +#define LSTAT_ACT_REJECT 3 +#define LSTAT_ACT_MAX_VALUES 4 + +/* + * Special values for the low 2 bits of an RA passed to + * lstat_update. + */ +#define LSTAT_RA_SPIN 0 +#define LSTAT_RA_RWSPIN 1 +#define LSTAT_RA_SEMA 2 + +/* + * Constants used for lock addresses in the lstat_directory + * to indicate special values of the lock address. + */ +#define LSTAT_MULTI_LOCK_ADDRESS NULL + +/* + * Maximum size of the lockstats tables. Increase this value + * if its not big enough. (Nothing bad happens if its not + * big enough although some locks will not be monitored.) + * We record overflows of this quantity in lstat_control.dir_overflows + * + * Note: This can be at most 32767 unless the size of + * the index field in the lock is changed. See INDEX_MASK + * in this file. + */ +#define LSTAT_MAX_STAT_INDEX 2000 + +/* + * This defines an entry in the lockstat directory. It contains + * information about a lock being monitored. + * A directory entry only contains the lock identification - + * counts on usage of the lock are kept elsewhere in a per-cpu + * data structure to minimize cache line pinging. + */ +typedef struct { + void *caller_ra; /* RA of function that set lock */ + void *lock_ptr; /* lock address */ + ushort next_stat_index; /* Used to link multiple locks that have the same hash table value */ +} lstat_directory_entry_t; + +/* + * A multi-dimensioned array used to contain counts for lock accesses. + * The array is 3-dimensional: + * - CPU number. Keep from thrashing cache lines between CPUs + * - Directory entry index. Identifies the lock + * - Action. Indicates what kind of contention occurred on an + * access to the lock. + * + * The index of an entry in the directory is the same as the 2nd index + * of the entry in the counts array. + */ +typedef struct { + uint64_t cum_wait_ticks; /* cum sum of wait times */ + uint64_t cum_hold_ticks; /* cum sum of hold times */ + uint32_t max_wait_ticks; /* max waiting time */ + uint32_t max_hold_ticks; /* max holding time */ + uint32_t acquire_time; /* time lock acquired this CPU */ + uint32_t count[LSTAT_ACT_MAX_VALUES]; +} lstat_lock_counts_t; + +typedef lstat_lock_counts_t lstat_cpu_counts_t[LSTAT_MAX_STAT_INDEX]; + +/* + * User request to: + * - turn statistic collection on/off, or to reset + */ +#define LSTAT_OFF 0 +#define LSTAT_ON 1 + +#define LSTAT_VERSION 2 + +typedef struct { + short lstat_version; /* version of the data */ + short lstat_is_enabled; /* the current state is returned */ + int maxcpus; /* Number of cpus present */ + int next_free_dir_index; /* index of the next free directory entry */ + time_t current_time; /* current time in secs since 1969 */ + uint cycleval; /* cycles per second */ + unsigned long enabled_jiffies; /* jiffies when collection was enabled */ + unsigned long current_jiffies; /* current value of jiffies */ + void *kernel_magic_addr; /* address of kernel_magic */ + void *kernel_end_addr; /* contents of kernel magic (points to "end") */ + lstat_cpu_counts_t *cpu_counts_ptr; /* pointer to the user buffer for returning counts */ + lstat_directory_entry_t *directory_ptr; /* pointer to the user buffer for returning dir info */ +} lstat_user_request_t; + + +#if defined(__KERNEL__) || defined(USER_MODE_TESTING) + +#include + +#define LSTAT_RA(n) \ + ((void*)( ((unsigned long)__builtin_return_address(0) & ~3) | n) ) + +/* + * Size and mask for the hash table into the directory. + */ +#define LSTAT_HASH_TABLE_SIZE 4096 /* must be 2**N */ +#define LSTAT_HASH_TABLE_MASK (LSTAT_HASH_TABLE_SIZE-1) + +#define DIRHASH(ra) ((unsigned long)(ra)>>2 & LSTAT_HASH_TABLE_MASK) + +/* + * This version eliminates the per processor lock stack. What we do is to + * store the index of the lock hash structure in unused bits in the lock + * itself. Then on unlock we can find the statistics record without doing + * any additional hash or lock stack lookup. This works for spin_locks. + * Hold time reporting is now basically as cheap as wait time reporting + * so we ignore the difference between LSTAT_ON, LSTAT_ON_HOLD, LSTAT_ON_WAIT + */ + +/* + * Main control structure for lockstat. Used to turn statistics on/off + * and to maintain directory info. + */ +typedef struct { + int state; + spinlock_t control_lock; /* used to serialize turning statistics on/off */ + spinlock_t directory_lock; /* used to serialize adding new entries to the directory */ + int next_free_dir_index; /* next free entry in the directory */ + unsigned long enabled_jiffies; /* jiffies when collection was enabled */ + lstat_directory_entry_t *dir; /* directory */ + ushort *hashtab; /* hash table for quick dir scans */ + lstat_cpu_counts_t *counts[NR_CPUS]; /* Array of pointers to per-cpu stats */ + /* per-cpu acquire-lock stats */ +} lstat_control_t; + +/* Make sure that the lock is in its own cache line */ +#ifdef XXX_NOTYET +#pragma set field attribute lstat_control_t directory_lock align=128 +#endif + +#endif /* defined(__KERNEL__) || defined(USER_MODE_TESTING) */ + +#endif /* _LINUX_LOCKMETER_H */ diff -urN 2.4.0/include/linux/memleak_unwrap.h 2.4.0-ikd-1/include/linux/memleak_unwrap.h --- 2.4.0/include/linux/memleak_unwrap.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/linux/memleak_unwrap.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,124 @@ +#ifndef _MM_UNWRAP_H +#define _MM_UNWRAP_H + +#include + +#ifdef CONFIG_MEMLEAK + +#ifdef MEMLEAK_UNWRAP_PAGE +/* mm/page_alloc.c */ +#undef __alloc_pages +#undef __get_free_pages +#undef get_zeroed_page +#undef rmqueue +#undef __alloc_pages_limit +#define __alloc_pages(zonelist,order) \ + __alloc_pages_wrap((zonelist),(order),IDPTR) +#define __get_free_pages(gfpmask,order) \ + __get_free_pages_wrap((gfpmask),(order),IDPTR) +#define get_zeroed_page(gfpmask) \ + get_zeroed_page_wrap((gfpmask),IDPTR) +#define rmqueue(zone,order) \ + rmqueue_wrap((zone),(order),IDPTR) +#define __alloc_pages_limit(zone,order,limit,direct_reclaim) \ + __alloc_pages_limit_wrap((zone),(order),(limit),(direct_reclaim),IDPTR) +#endif /* MEMLEAK_UNWRAP_PAGE */ + +#ifdef MEMLEAK_UNWRAP_SLAB +/* mm/slab.c */ +/* + * NOTE: leave kmem_cache_create wrapped, as otherwise the allocation + * id won't exist for the underlying allocator. Other functions + * which lead to a physical allocation must also pass this id. + * This looks ugly, but causes ownership of the allocation to be + * passed on to the allocation initiator. + * + * freeing of the allocation is the responsibility of the underlying + * allocator. I hope that this helps to keep memleak in sync. + */ + +#undef kmem_cache_alloc +#undef kmalloc + +#define kmem_cache_alloc(cachep,flags) \ + kmem_cache_alloc_wrap((cachep),(flags),IDPTR) + +#define kmalloc(size,priority) \ + kmalloc_wrap((size),(priority),IDPTR) + +#define kmem_cache_grow(cachep,flags) \ + kmem_cache_grow_wrap((cachep),(flags),IDPTR) + +#define __kmem_cache_alloc(cachep,flags) \ + __kmem_cache_alloc_wrap((cachep),(flags),IDPTR) + +#define kmem_getpages(cachep,flags) \ + kmem_getpages_wrap((cachep),(flags),IDPTR) + +#define kmem_cache_slabmgmt(cachep,objp,offset,local_flags) \ + kmem_cache_slabmgmt_wrap((cachep),(objp),(offset),(local_flags),IDPTR) +#endif /* MEMLEAK_UNWRAP_SLAB */ + +#ifdef MEMLEAK_UNWRAP_VMALLOC +/* mm/vmalloc.c */ +#undef get_vm_area +#undef alloc_area_pte +#undef alloc_area_pmd +#undef vmalloc_area_pages + +#define get_vm_area(size,flags) get_vm_area_wrap((size),(flags),IDPTR) +#define alloc_area_pte(pte, address, size, gfpmask, prot) \ + alloc_area_pte_wrap((pte),(address),(size),(gfpmask),(prot),IDPTR) +#define alloc_area_pmd(pmd, address, size, gfpmask, prot) \ + alloc_area_pmd_wrap((pmd),(address),(size),(gfpmask),(prot),IDPTR) +#define vmalloc_area_pages(address, size, gfpmask, prot) \ + vmalloc_area_pages_wrap((address),(size),(gfpmask),(prot),IDPTR) +#endif /* MEMLEAK_UNWRAP_VMALLOC */ + +#ifdef MEMLEAK_UNWRAP_SKBUFF +/* net/core/skbuff.c */ +#undef alloc_skb +#undef skb_clone +#undef skb_copy +#undef skb_copy_expand + +#define alloc_skb(size,gfp_mask) alloc_skb_wrap((size),(gfp_mask),IDPTR) +#define skb_clone(skb,gfp_mask) skb_clone_wrap((skb),(gfp_mask),IDPTR) +#define skb_copy(skb,gfp_mask) skb_copy_wrap((skb),(gfp_mask),IDPTR) +#define skb_copy_expand(skb,newheadroom,newtailroom,priority) \ + skb_copy_expand_wrap((skb),(newheadroom),(newtailroom),(priority),IDPTR) +#endif /* MEMLEAK_UNWRAP_SKBUFF */ + +#ifdef MEMLEAK_UNWRAP_SOCK +/* net/core/sock.c */ +#undef sock_wmalloc +#undef sock_rmalloc +#undef sock_kmalloc +#undef sk_alloc + +#define sock_wmalloc(sk,size,force,priority) sock_wmalloc_wrap((sk),(size),(force),(priority),IDPTR) +#define sock_rmalloc(sk,size,force,priority) sock_rmalloc_wrap((sk),(size),(force),(priority),IDPTR) +#define sock_kmalloc(sk,size,priority) sock_kmalloc_wrap((sk),(size),(priority),IDPTR) +#define sk_alloc(family,priority,zero_it) sk_alloc_wrap((family),(priority),(zero_it),IDPTR) + +/* include/net/sock.h */ +#undef sock_alloc_send_skb +#define sock_alloc_send_skb(sk,size,fallback,noblock,errcode) \ + sock_alloc_send_skb_wrap((sk),(size),(fallback),(noblock),(errcode),IDPTR) +#endif /* MEMLEAK_UNWRAP_SOCK */ + +#ifdef MEMLEAK_UNWRAP_NUMA +/* mm/numa.c */ +#undef alloc_pages_node +#define alloc_pages_node(nid,gfp_mask,order) \ + alloc_pages_node_wrap((nid),(gfp_mask),(order),IDPTR) +#ifdef CONFIG_DISCONTIGMEM +#undef alloc_pages +#define alloc_pages(gfp_mask,order) \ + alloc_pages_wrap((gfp_mask),(order),IDPTR) +#endif + +#endif /* MEMLEAK_UNWRAP_NUMA */ + +#endif /* CONFIG_MEMLEAK */ +#endif /* _MM_UNWRAP_H */ diff -urN 2.4.0/include/linux/mm.h 2.4.0-ikd-1/include/linux/mm.h --- 2.4.0/include/linux/mm.h Mon Jan 15 01:55:12 2001 +++ 2.4.0-ikd-1/include/linux/mm.h Mon Jan 15 18:40:04 2001 @@ -337,6 +337,7 @@ * can allocate highmem pages, the *get*page*() variants return * virtual kernel addresses to the allocated page(s). */ +#ifndef CONFIG_MEMLEAK extern struct page * FASTCALL(__alloc_pages(zonelist_t *zonelist, unsigned long order)); extern struct page * alloc_pages_node(int nid, int gfp_mask, unsigned long order); @@ -364,6 +365,118 @@ #define __get_dma_pages(gfp_mask, order) \ __get_free_pages((gfp_mask) | GFP_DMA,(order)) + +#define MEMLEAK_ALLOC(addr) do {} while(0) +#define MEMLEAK_FREE(ddr) do {} while(0) +#define MEMLEAK_ALLOC_NOLOCK(addr) do {} while(0) +#define MEMLEAK_FREE_TRUE(expr,addr) do {} while(0) + +#else +/* + * 'allocation identifier' for memleak detection + */ +struct alloc_struct { + int id; + char *file; + int line; +}; + +#define MEMLEAK_WRAP(x,y...) \ +({ \ + static struct alloc_struct MEMLEAKID = { 0, __FILE__, __LINE__ }; \ + x##_wrap(y,&MEMLEAKID); \ +}) + +extern void memleak_init (void); +extern int alloc_addr_lock(unsigned long, struct alloc_struct *); +extern int alloc_addr_nolock(unsigned long, struct alloc_struct *); +extern int free_addr(unsigned long); + +#define MEMLEAK_PARANOID 1 +#ifdef MEMLEAK_PARANOID +extern int memleak_errcount; +#define PROBLEM() do { \ +if (memleak_errcount++ < 10) \ + printk(KERN_ERR "MEMLEAK PROBLEM: <%s,%d>.\n",__FILE__,__LINE__); \ +} while(0) +#else +#define PROBLEM() do {} while(0) +#endif /* MEMLEAK_PARANOID */ + +extern struct page * __alloc_pages_wrap(zonelist_t *zonelist, unsigned long order, struct alloc_struct *IDPTR); +extern struct page * alloc_pages_node_wrap(int nid, int gfp_mask, unsigned long order, struct alloc_struct *IDPTR); +extern unsigned long __get_free_pages_wrap(int gfp_mask, unsigned long order, struct alloc_struct *IDPTR); +extern unsigned long get_zeroed_page_wrap(int gfp_mask, struct alloc_struct *IDPTR); + +#define __alloc_pages(zonelist, order) \ + MEMLEAK_WRAP(__alloc_pages,(zonelist),(order)) + +#define alloc_pages_node(nid, gfp_mask, order) \ + MEMLEAK_WRAP(alloc_pages_node,(nid),(gfp_mask),(order)) + +#define __get_free_pages(gfp_mask, order) \ + MEMLEAK_WRAP(__get_free_pages,(gfp_mask),(order)) + +#define get_zeroed_page(gfp_mask) \ + MEMLEAK_WRAP(get_zeroed_page,(gfp_mask)) + +#ifndef CONFIG_DISCONTIGMEM +#define alloc_pages(gfp_mask, order) \ +({ \ + struct page *__ret = NULL; \ + /* \ + * Gets optimized away by the compiler. \ + */ \ + if ((order) >= MAX_ORDER) \ + goto alloc_pages_out; \ + __ret = __alloc_pages(contig_page_data.node_zonelists+(gfp_mask), (order)); \ +alloc_pages_out: \ + __ret; \ +}) +#else +extern struct page * alloc_pages_wrap(int gfp_mask, unsigned long order, struct alloc_struct *IDPTR); +#define alloc_pages(gfpmask, order) \ + MEMLEAK_WRAP(alloc_pages,(gfpmask),(order)) +#endif /* CONFIG_DISCONTIGMEM */ + +#define alloc_page(gfpmask) \ + alloc_pages((gfpmask), 0) + +#define __get_free_page(gfpmask) \ + __get_free_pages((gfpmask),0) + +#define __get_dma_pages(gfpmask, order) \ + __get_free_pages((gfpmask) | GFP_DMA,(order)) + +#ifndef MEMLEAK_PASS_ALLOCATION +/* These are for use externally to an allocator. All allocators pass a + * pointer down the stack and map the allocation from inside the alocator, + * and under it's locking mechanism. + */ +#define MEMLEAK_ALLOC(addr) do { \ + if(alloc_addr_lock((unsigned long)(addr),&MEMLEAKID)) \ + PROBLEM(); \ +} while(0) +#else +#define MEMLEAK_ALLOC(addr) do { \ + if(alloc_addr_lock((unsigned long)(addr),IDPTR)) \ + PROBLEM(); \ +} while(0) +#define MEMLEAK_ALLOC_NOLOCK(addr) do { \ + if(alloc_addr_nolock((unsigned long)(addr),IDPTR)) \ + PROBLEM(); \ +} while(0) +#endif /* MEMLEAK_PASS_ALLOCATION */ + +#define MEMLEAK_FREE(addr) do { \ + if(free_addr((unsigned long)(addr))) \ + PROBLEM(); \ +} while(0) +#define MEMLEAK_FREE_TRUE(expr,addr) do { \ + if((expr)) \ + MEMLEAK_FREE((addr)); \ +} while(0) +#endif /* CONFIG_MEMLEAK */ /* * The old interface name will be removed in 2.5: diff -urN 2.4.0/include/linux/profiler.h 2.4.0-ikd-1/include/linux/profiler.h --- 2.4.0/include/linux/profiler.h Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/include/linux/profiler.h Mon Jan 15 18:40:04 2001 @@ -0,0 +1,92 @@ +#ifndef _LINUX_PROFILER_H +#define _LINUX_PROFILER_H + +#include +#include +#include +#include +#include + +#ifdef __KERNEL__ +#ifdef CONFIG_DEBUG_MCOUNT + +#ifdef __ia64__ +extern void mcount(profiler_pc_t, profiler_pc_t); +#else +extern void mcount (void); +#endif +extern int mcount_internal(profiler_pc_t self_addr); +extern atomic_t mcount_ready; /* controls all mcount() processing */ + +#define SUSPEND_MCOUNT atomic_dec(&mcount_ready) +#define RESUME_MCOUNT atomic_inc(&mcount_ready) +#define SUSPEND_MCOUNT_PROC(x) ((x)->flags |= PF_NO_MCOUNT) +#define RESUME_MCOUNT_PROC(x) ((x)->flags &= ~PF_NO_MCOUNT) +#define MCOUNT() mcount() + +#ifdef CONFIG_TRACE + +extern atomic_t mcount_trace_ready; /* controls just mcount() tracing */ +/* + * Protect the profiling table with a spin lock, only one cpu at a + * time. No point in read/write locks, almost all accesses are for + * write. Since this code is accessed from all contexts, use + * spin_lock_irqsave. + */ +extern spinlock_t trace_table_lock; + +/* Note: The hierarchy is mcount_ready, mcount_trace_ready, trace_table_lock */ + +struct trace_entry { + profiler_pc_t pc; +#ifdef CONFIG_TRACE_TIMESTAMP + profiler_timestamp_t timestamp; +#endif +#ifdef CONFIG_TRACE_PID + pid_t pid; +#endif +#if defined(CONFIG_TRACE_CPU) && defined(CONFIG_SMP) + unsigned int cpu; +#endif +}; + +extern struct trace_table { + unsigned int table_size; + unsigned int curr_call; + struct trace_entry entries[CONFIG_TRACE_SIZE]; +} *trace_table; + +/* + * die_if_kernel() uses this to 'extend' the stack trace given in an Oops + * message. You can use this when debugging special code, as a debugging aid. + */ +void print_emergency_trace (void); + +#define TRACE_CALIBRATION_CALLS 20 + +#define SUSPEND_MCOUNT_TRACE atomic_dec(&mcount_trace_ready) +#define RESUME_MCOUNT_TRACE atomic_inc(&mcount_trace_ready) +#define LOCK_MCOUNT_TRACE(x) spin_lock_irqsave(&trace_table_lock, x); +#define UNLOCK_MCOUNT_TRACE(x) spin_unlock_irqrestore(&trace_table_lock, x); + +#else /* !CONFIG_TRACE */ + +#define SUSPEND_MCOUNT_TRACE +#define RESUME_MCOUNT_TRACE +#define LOCK_MCOUNT_TRACE(x) +#define UNLOCK_MCOUNT_TRACE(x) + +#endif /* CONFIG_TRACE */ +#else /* !CONFIG_DEBUG_MCOUNT */ + +#define SUSPEND_MCOUNT +#define RESUME_MCOUNT +#define SUSPEND_MCOUNT_PROC(x) +#define RESUME_MCOUNT_PROC(x) +#define MCOUNT() + +#endif /* CONFIG_DEBUG_MCOUNT */ + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_PROFILER_H */ diff -urN 2.4.0/include/linux/reboot.h 2.4.0-ikd-1/include/linux/reboot.h --- 2.4.0/include/linux/reboot.h Mon Jan 15 01:55:14 2001 +++ 2.4.0-ikd-1/include/linux/reboot.h Mon Jan 15 18:40:04 2001 @@ -20,6 +20,9 @@ * CAD_OFF Ctrl-Alt-Del sequence sends SIGINT to init task. * POWER_OFF Stop OS and remove all power from system, if possible. * RESTART2 Restart system using given command string. + * OOPS Cause a kernel Oops, the machine should continue afterwards. + * STACKFAULT Overflow the kernel stack with recursion. + * KERNEL_LOOP Endless kernel loop, unlocked. */ #define LINUX_REBOOT_CMD_RESTART 0x01234567 @@ -29,6 +32,9 @@ #define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC #define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4 +#define LINUX_REBOOT_CMD_OOPS 0x4F6F7001 +#define LINUX_REBOOT_CMD_STACKFAULT 0x53746602 +#define LINUX_REBOOT_CMD_KERNEL_LOOP 0x4C6F7003 #ifdef __KERNEL__ diff -urN 2.4.0/include/linux/sched.h 2.4.0-ikd-1/include/linux/sched.h --- 2.4.0/include/linux/sched.h Mon Jan 8 06:32:58 2001 +++ 2.4.0-ikd-1/include/linux/sched.h Mon Jan 15 18:40:04 2001 @@ -394,6 +394,9 @@ u32 self_exec_id; /* Protection of (de-)allocation: mm, files, fs, tty */ spinlock_t alloc_lock; +#ifdef CONFIG_DEBUG_SOFTLOCKUP + unsigned int deadlock_count; +#endif }; /* @@ -409,6 +412,9 @@ #define PF_SIGNALED 0x00000400 /* killed by a signal */ #define PF_MEMALLOC 0x00000800 /* Allocating memory */ #define PF_VFORK 0x00001000 /* Wake up parent in mm_release */ +#ifdef CONFIG_DEBUG_MCOUNT +#define PF_NO_MCOUNT 0x00002000 /* skip mcount() processing */ +#endif #define PF_USEDFPU 0x00100000 /* task used FPU this quantum (SMP) */ diff -urN 2.4.0/include/linux/skbuff.h 2.4.0-ikd-1/include/linux/skbuff.h --- 2.4.0/include/linux/skbuff.h Mon Jan 15 01:55:26 2001 +++ 2.4.0-ikd-1/include/linux/skbuff.h Mon Jan 15 18:40:04 2001 @@ -159,6 +159,8 @@ extern void __kfree_skb(struct sk_buff *skb); extern struct sk_buff * skb_peek_copy(struct sk_buff_head *list); + +#ifndef CONFIG_MEMLEAK extern struct sk_buff * alloc_skb(unsigned int size, int priority); extern void kfree_skbmem(struct sk_buff *skb); extern struct sk_buff * skb_clone(struct sk_buff *skb, int priority); @@ -167,6 +169,27 @@ int newheadroom, int newtailroom, int priority); +#else /* CONFIG_MEMLEAK */ + +#include + +extern struct sk_buff * alloc_skb_wrap(unsigned int size, int priority, struct alloc_struct * IDPTR); +extern void kfree_skbmem(struct sk_buff *skb); /* no wrap for this */ +extern struct sk_buff * skb_clone_wrap(struct sk_buff *skb, int priority, struct alloc_struct * IDPTR); +extern struct sk_buff * skb_copy_wrap(const struct sk_buff *skb, int priority, struct alloc_struct * IDPTR); +extern struct sk_buff * skb_copy_expand_wrap(const struct sk_buff *skb, + int newheadroom, + int newtailroom, + int priority, + struct alloc_struct * IDPTR); + +#define alloc_skb(size,priority) MEMLEAK_WRAP(alloc_skb,(size),(priority)) +#define skb_clone(skb,priority) MEMLEAK_WRAP(skb_clone,(skb),(priority)) +#define skb_copy(skb,priority) MEMLEAK_WRAP(skb_copy,(skb),(priority)) +#define skb_copy_expand(skb,newheadroom,newtailroom,priority) \ + MEMLEAK_WRAP(skb_copy_expand,(skb),(newheadroom),(newtailroom),(priority)) +#endif /* CONFIG_MEMLEAK */ + #define dev_kfree_skb(a) kfree_skb(a) extern void skb_over_panic(struct sk_buff *skb, int len, void *here); extern void skb_under_panic(struct sk_buff *skb, int len, void *here); @@ -890,6 +913,7 @@ * allocates memory it can be called from an interrupt. */ +#ifndef CONFIG_MEMLEAK static inline struct sk_buff *dev_alloc_skb(unsigned int length) { struct sk_buff *skb; @@ -899,6 +923,16 @@ skb_reserve(skb,16); return skb; } +#else +#define dev_alloc_skb(length) \ +({ \ + struct sk_buff *_skb; \ + _skb = alloc_skb((unsigned int)(length)+16, GFP_ATOMIC); \ + if (_skb) \ + skb_reserve(_skb,16); \ + _skb; \ +}) +#endif /* CONFIG_MEMLEAK */ /** * skb_cow - copy a buffer if need be diff -urN 2.4.0/include/linux/slab.h 2.4.0-ikd-1/include/linux/slab.h --- 2.4.0/include/linux/slab.h Mon Jan 15 01:55:13 2001 +++ 2.4.0-ikd-1/include/linux/slab.h Mon Jan 15 18:40:04 2001 @@ -9,6 +9,8 @@ #if defined(__KERNEL__) +#include + typedef struct kmem_cache_s kmem_cache_t; #include @@ -45,8 +47,9 @@ /* prototypes */ extern void kmem_cache_init(void); extern void kmem_cache_sizes_init(void); - extern kmem_cache_t *kmem_find_general_cachep(size_t, int gfpflags); + +#ifndef CONFIG_MEMLEAK extern kmem_cache_t *kmem_cache_create(const char *, size_t, size_t, unsigned long, void (*)(void *, kmem_cache_t *, unsigned long), void (*)(void *, kmem_cache_t *, unsigned long)); @@ -57,6 +60,28 @@ extern void *kmalloc(size_t, int); extern void kfree(const void *); +#else /* CONFIG_MEMLEAK */ +extern kmem_cache_t *kmem_cache_create_wrap(const char *, size_t, size_t, unsigned long, + void (*)(void *, kmem_cache_t *, unsigned long), + void (*)(void *, kmem_cache_t *, unsigned long), struct alloc_struct *); +extern int kmem_cache_shrink(kmem_cache_t *); /* no wrap for this */ +extern int kmem_cache_destroy(kmem_cache_t *); /* no wrap for this */ +extern void *kmem_cache_alloc_wrap(kmem_cache_t *, int, struct alloc_struct *); +extern void kmem_cache_free(kmem_cache_t *, void *); /* no wrap for this */ + +extern void *kmalloc_wrap(unsigned int size, int priority, struct alloc_struct *); +extern void kfree(const void *); + +#define kmem_cache_create(name,size,offset,flags,ctor,dtor) \ + MEMLEAK_WRAP(kmem_cache_create,name,size,offset,flags,ctor,dtor) + +#define kmem_cache_alloc(cachep,flags) \ + MEMLEAK_WRAP(kmem_cache_alloc,cachep,flags) + +#define kmalloc(size,priority) \ + MEMLEAK_WRAP(kmalloc,size,priority) + +#endif /* CONFIG_MEMLEAK */ extern void kmem_cache_reap(int); extern int slabinfo_read_proc(char *page, char **start, off_t off, diff -urN 2.4.0/include/linux/string.h 2.4.0-ikd-1/include/linux/string.h --- 2.4.0/include/linux/string.h Sun Jan 14 16:33:41 2001 +++ 2.4.0-ikd-1/include/linux/string.h Mon Jan 15 18:40:04 2001 @@ -75,6 +75,7 @@ #ifndef __HAVE_ARCH_MEMCHR extern void * memchr(const void *,int,__kernel_size_t); #endif +extern char * kstrdup(const char *,int); #ifdef __cplusplus } diff -urN 2.4.0/include/linux/sysctl.h 2.4.0-ikd-1/include/linux/sysctl.h --- 2.4.0/include/linux/sysctl.h Sun Jan 14 16:33:52 2001 +++ 2.4.0-ikd-1/include/linux/sysctl.h Mon Jan 15 18:40:05 2001 @@ -117,6 +117,8 @@ KERN_OVERFLOWGID=47, /* int: overflow GID */ KERN_SHMPATH=48, /* string: path to shm fs */ KERN_HOTPLUG=49, /* string: path to hotplug policy agent */ + KERN_NMI_WATCHDOG=50, /* int: nmi_watchdog on/off */ + KERN_KDB=51, /* int: kdb on/off */ }; @@ -529,6 +531,10 @@ }; /* CTL_DEBUG names: */ +enum { + DEBUG_KSTACK_METER = 1, + DEBUG_DISABLE_MCOUNT = 2, +}; /* CTL_DEV names: */ enum { diff -urN 2.4.0/include/linux/vmalloc.h 2.4.0-ikd-1/include/linux/vmalloc.h --- 2.4.0/include/linux/vmalloc.h Mon Jan 15 01:55:13 2001 +++ 2.4.0-ikd-1/include/linux/vmalloc.h Mon Jan 15 18:40:05 2001 @@ -18,14 +18,34 @@ struct vm_struct * next; }; +#ifndef CONFIG_MEMLEAK extern struct vm_struct * get_vm_area (unsigned long size, unsigned long flags); +#else +extern struct vm_struct * get_vm_area_wrap (unsigned long size, + unsigned long flags, struct alloc_struct *id); +#define get_vm_area(size,flags) MEMLEAK_WRAP(get_vm_area,size,flags) +#endif extern void vfree(void * addr); +#ifndef CONFIG_MEMLEAK extern void * __vmalloc (unsigned long size, int gfp_mask, pgprot_t prot); +#else +extern void * __vmalloc_wrap (unsigned long size, int gfp_mask, + pgprot_t prot, struct alloc_struct *id); +#define __vmalloc(size,gfpmask,prot) MEMLEAK_WRAP(__vmalloc,size,gfpmask,prot) +#endif extern long vread(char *buf, char *addr, unsigned long count); extern void vmfree_area_pages(unsigned long address, unsigned long size); -extern int vmalloc_area_pages(unsigned long address, unsigned long size, - int gfp_mask, pgprot_t prot); +#ifndef CONFIG_MEMLEAK +extern int vmalloc_area_pages(unsigned long address, unsigned long size , int gfp_mask, pgprot_t prot); +#else +extern int vmalloc_area_pages_wrap(unsigned long address, + unsigned long size, int gfp_mask, pgprot_t prot, + struct alloc_struct *id); +#define vmalloc_area_pages(addr,size,gfpmask) \ + MEMLEAK_WRAP(vmalloc_area_pages,addr,size,gfpmask,prot) +#endif +#ifndef CONFIG_MEMLEAK /* * Allocate any pages */ @@ -53,12 +73,20 @@ return __vmalloc(size, GFP_KERNEL, PAGE_KERNEL); } +#else /*CONFIG_MEMLEAK*/ +#define vmalloc(size) \ + __vmalloc((size),(GFP_KERNEL|__GFP_HIGHMEM),PAGE_KERNEL) +#define vmalloc_dma(size) \ + __vmalloc((size), (GFP_KERNEL|GFP_DMA),PAGE_KERNEL) +#define vmalloc_32(size) __vmalloc((size), GFP_KERNEL,PAGE_KERNEL) +#endif /*CONFIG_MEMLEAK*/ + /* * vmlist_lock is a read-write spinlock that protects vmlist * Used in mm/vmalloc.c (get_vm_area() and vfree()) and fs/proc/kcore.c. */ extern rwlock_t vmlist_lock; - + extern struct vm_struct * vmlist; #endif diff -urN 2.4.0/include/net/sock.h 2.4.0-ikd-1/include/net/sock.h --- 2.4.0/include/net/sock.h Mon Jan 15 01:55:28 2001 +++ 2.4.0-ikd-1/include/net/sock.h Mon Jan 15 18:40:05 2001 @@ -812,15 +812,38 @@ return a; } +#ifndef CONFIG_MEMLEAK extern struct sock * sk_alloc(int family, int priority, int zero_it); +#endif extern void sk_free(struct sock *sk); +#ifndef CONFIG_MEMLEAK extern struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, int priority); extern struct sk_buff *sock_rmalloc(struct sock *sk, unsigned long size, int force, int priority); +#else +extern struct sk_buff *sock_wmalloc_wrap(struct sock *sk, + unsigned long size, int force, + int priority, struct alloc_struct *IDPTR); +extern struct sk_buff *sock_rmalloc_wrap(struct sock *sk, + unsigned long size, int force, + int priority, struct alloc_struct *IDPTR); +extern void *sock_kmalloc_wrap(struct sock *sk, int size, int priority, struct alloc_struct *IDPTR); +extern struct sock * sk_alloc_wrap(int family, int priority, int zero_it, struct alloc_struct *IDPTR); + +#define sock_wmalloc(sk,size,force,priority) \ + MEMLEAK_WRAP(sock_wmalloc,(sk),(size),(force),(priority)) +#define sock_rmalloc(sk,size,force,priority) \ + MEMLEAK_WRAP(sock_rmalloc,(sk),(size),(force),(priority)) +#define sock_kmalloc(sk,size,priority) \ + MEMLEAK_WRAP(sock_kmalloc,(sk),(size),(priority)) +#define sk_alloc(family,priority,zero_it) \ + MEMLEAK_WRAP(sk_alloc,(family),(priority),(zero_it)) + +#endif /* CONFIG_MEMLEAK */ extern void sock_wfree(struct sk_buff *skb); extern void sock_rfree(struct sk_buff *skb); @@ -831,12 +854,23 @@ extern int sock_getsockopt(struct socket *sock, int level, int op, char *optval, int *optlen); +#ifndef CONFIG_MEMLEAK extern struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigned long fallback, int noblock, int *errcode); extern void *sock_kmalloc(struct sock *sk, int size, int priority); +#else +extern struct sk_buff *sock_alloc_send_skb_wrap(struct sock *sk, + unsigned long size, + unsigned long fallback, + int noblock, + int *errcode, + struct alloc_struct *IDPTR); +#define sock_alloc_send_skb(sk,size,fallback,noblock,errcode) \ + MEMLEAK_WRAP(sock_alloc_send_skb,sk,size,fallback,noblock,errcode) +#endif extern void sock_kfree_s(struct sock *sk, void *mem, int size); extern int copy_and_csum_toiovec(struct iovec *iov, struct sk_buff *skb, int hlen); diff -urN 2.4.0/init/main.c 2.4.0-ikd-1/init/main.c --- 2.4.0/init/main.c Fri Jan 5 02:19:29 2001 +++ 2.4.0-ikd-1/init/main.c Mon Jan 15 18:40:05 2001 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -67,6 +68,10 @@ #include #endif +#if defined(CONFIG_KDB) +#include +#endif + /* * Versions of gcc older than that listed below may actually compile * and link okay, but the end product can have subtle run time bugs. @@ -111,6 +116,14 @@ extern void dquot_init_hash(void); #endif +#ifdef __sparc__ +extern int serial_console; +#endif + +#ifdef CONFIG_DEBUG_MCOUNT +extern void mcount_init(void); +#endif + /* * Boot command-line arguments */ @@ -452,6 +465,34 @@ } if (next != NULL) *next++ = 0; +#if defined(CONFIG_KDB) + /* kdb, kdb=on, kdb=off, kdb=early */ + if (strncmp(line, "kdb", 3) == 0) { + if (line[3] == '\0') { + /* Backward compatibility, kdb with no option means early activation */ + printk("Boot flag kdb with no options is obsolete, use kdb=early\n"); + kdb_on = 1; + kdb_flags |= KDB_FLAG_EARLYKDB; + continue; + } + if (line[3] == '=') { + if (strcmp(line+4, "on") == 0) { + kdb_on = 1; + continue; + } + if (strcmp(line+4, "off") == 0) { + kdb_on = 0; + continue; + } + if (strcmp(line+4, "early") == 0) { + kdb_on = 1; + kdb_flags |= KDB_FLAG_EARLYKDB; + continue; + } + printk("Boot flag %s not recognised, assumed to be environment variable\n", line); + } + } +#endif if (!strncmp(line,"init=",5)) { line += 5; execute_command = line; @@ -531,6 +572,16 @@ setup_arch(&command_line); printk("Kernel command line: %s\n", saved_command_line); parse_options(command_line); +#ifdef CONFIG_MEMLEAK + /* + * memleak_init must run before other xx_init() will start + * eating ram. + */ + memleak_init(); +#endif +#ifdef CONFIG_DEBUG_MCOUNT + mcount_init(); +#endif trap_init(); init_IRQ(); sched_init(); @@ -559,6 +610,12 @@ kmem_cache_init(); sti(); calibrate_delay(); +#if defined(CONFIG_KDB) + kdb_init(); + if (KDB_FLAG(EARLYKDB)) { + KDB_ENTER(); + } +#endif #ifdef CONFIG_BLK_DEV_INITRD if (initrd_start && !initrd_below_start_ok && initrd_start < min_low_pfn << PAGE_SHIFT) { @@ -638,6 +695,32 @@ flush_scheduled_tasks(); } +#if defined(CONFIG_SMP) && defined(CONFIG_KERNEL_DEBUGGING) +void show_one (int i) +{ + static int curr=0x12345678; + + curr++; + *(((volatile int *)0x000b8000)+i)=curr; + *(((volatile int *)0x000b8100)+i)=curr; + *(((volatile int *)0x000b8200)+i)=curr; + *(((volatile int *)0x000b8300)+i)=curr; +} + +void show_us(void) +{ + for (;;) { + __cli(); + show_one(0); + show_one(10); + show_one(20); + show_one(30); + show_one(40); + show_one(50); + } +} +#endif + /* * Ok, the machine is now initialized. None of the devices * have been touched yet, but the CPU subsystem is up and @@ -768,7 +851,9 @@ * we're essentially up and running. Get rid of the * initmem segments and start the user-mode stuff.. */ +#ifndef CONFIG_MEMLEAK free_initmem(); +#endif unlock_kernel(); if (open("/dev/console", O_RDWR, 0) < 0) diff -urN 2.4.0/kdb/ChangeLog 2.4.0-ikd-1/kdb/ChangeLog --- 2.4.0/kdb/ChangeLog Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kdb/ChangeLog Mon Jan 15 18:40:05 2001 @@ -0,0 +1,332 @@ +2000-11-18 Keith Owens + + * Update to kernel 2.4.0-test11-pre7, including forward port of + bug fixes from WIP 2.4.0-test9 tree. + + * Update to Cygnus CVS trees for disassembly code. + + * Bump to kdb v1.6. + +2000-10-19 Keith Owens + + * Update to kernel 2.4.0-test10-pre4. + +2000-10-15 Keith Owens + + * kdb/kdbmain.c (kdb_parse): Correctly handle blank input. + + * kdb/kdbmain.c (kdb_local, kdb): Reason SILENT can have NULL ef. + +2000-10-13 Keith Owens + + * kdb/kdbmain.c: Reduce CMD_LEN to avoid overflowing kdb_printf buffer. + +2000-10-11 Keith Owens + + * kdb/kdbmain.c (kdb): Test for userspace breakpoints before driving + other cpus into kdb. Speeds up gdb and avoids SMP race. + + * arch/i386/kdb/kdba_io.c (get_serial_char, get_kbd_char): Ignore + unprintable characters. + + * arch/i386/kdb/kdba_io.c (kdba_read): Better handling of buffer size. + +2000-10-04 Keith Owens + + * arch/i386/kdb/kdba_bt.c (kdba_bt_process): Verify that esp is inside + task_struct. Original patch by Mike Galbraith. + + * kdb/kdb_io.c (kdb_getstr): Reset output line counter, remove + unnecessary prompts. + + * arch/i386/kdb/kdbasupport.c (kdb_getregcontents): Change " cs" to + "xcs", ditto ss, ds, es. gdb2kdb does not like leading spaces. + + * include/asm-xxx/kdb.h: Add dummy kdb.h for all architectures except + ix86. This allows #include to appear in arch independent + code without causing compile errors. + + * kdb/modules/kdbm_pg: Sync with XFS. + +2000-10-03 Keith Owens + + * kdb/kdb_io.c (kdb_read): Ignore NMI while waiting for input. + + * kdb/kdb_io.c, kdb/Makefile: Export kdb_read. + +2000-10-02 Keith Owens + + * arch/i386/kernel/smpboot.c (do_boot_cpu): Set nmi_watchdog_source to 2 + to avoid premature NMI oops during cpu bring up. We have to assume that + a box with more than 1 cpu has a working IO-APIC. + + * Documentation/kdb/{kdb.mm,kdb_md.man}: Add mdr command. + + * kdb/kdbmain.c (kdb_md): Add mdr command. + + * Release as kdb v1.5 against 2.4.0-test9-pre8. + + * arch/i386/kdb/kdba_io.c, arch/i386/kdb/kdbasupport.c, kdb/kdbmain.c, + kdb/kdb_io.c, kdb/kdb_id.c: Remove zero initializers for static + variables. + +2000-09-28 Keith Owens + + * various: Add nmi_watchdog_source, 1 local APIC, 2 IO-APIC. + Test nmi_watchdog_source instead of nr_ioapics so UP works on SMP hardware. + + * arch/i386/kernel/io_apic.c: Rename setup_nmi to setup_nmi_io for clarity. + + * kdb/kdbmain.c (kdb_parse): Only set NO_WATCHDOG if it was already set. + + * kdb/kdbmain.c (kdb): Clear NO_WATCHDOG on all exit paths. + + * include/linux/kdb.h: Add KDB_REASON_SILENT. + + * kdb/kdbmain.c (kdb_local): Treat reason SILENT as immediate 'go'. + + * kdb/kdbmain.c (kdb_init): Invoke kdb with reason SILENT to instantiate + any breakpoints on boot cpu. + + * arch/i386/kernel/smpboot.c (smp_callin): Invoke kdb with reason SILENT + to instantiate any global breakpoints on this cpu. + + * kdb/kdb_cmds: Remove comment that said initial commands only worked on + boot cpu. + +2000-09-27 Keith Owens + + * arch/i386/kernel/msr.c: Move {rd,wr}msr_eio to include/asm-i386/apic.h. + + * include/asm-i386/apic.h: Define NMI interfaces. + + * kernel/sysctl.c (kern_table): + * kernel/sysctl.c (do_proc_set_nmi_watchdog): + Add /proc/sys/kernel/nmi_watchdog. + + * arch/i386/kernel/apic.c: New routines set_nmi_counter_local, + setup_apic_nmi_watchdog. + + * arch/i386/kernel/traps.c: New routine set_nmi_watchdog(). Call apic + routines to set/clear local apic timer. + +2000-09-26 Keith Owens + + * include/linux/sysctl.h (enum): Add NMI_WATCHDOG. + + * arch/i386/kernel/traps.c (nmi_watchdog_tick): Check nmi_watchdog is + still on. + + * arch/i386/config.in: Add CONFIG_UP_NMI_WATCHDOG. + + * Documentation/Configure.help: Add CONFIG_UP_NMI_WATCHDOG. + + * Documentation/nmi_watchdog.txt: Update for UP NMI watchdog. + +2000-09-25 Keith Owens + + * arch/i386/kernel/apic.c (init_apic_mappings): + * arch/i386/kernel/io_apic.c (IO_APIC_init_uniprocessor): + Merge Keir Fraser's local APIC for uniprocessors patch. + +2000-09-24 Keith Owens + + * Various: Declare initialization routines as __init. + + * Makefile: Define and export AWK. + + * kdb/Makefile: Generate gen-kdb_cmds.c from kdb/kdb_cmds. + + * kdb/kdbmain.c (kdb_init): Call new routine kdb_cmds_init to execute + whatever the user put in kdb/kdb_cmds. + + * arch/i386/kdb/kdba_bt.c (kdba_bt_stack): New parameter to + indicate if esp in regs is known to be valid or not. + + * kdb/kdb_bp.c, arch/i386/kdb/kdba_bp.c: More trace prints for + breakpoint handling. + + * arch/i386/kdb/kdba_bp.c (kdba_installbp): Finally found and fixed the + annoying breakpoint bug where breakpoints where not always installed + after 'go'. + + * Documentation/kdb: Update man pages kdb.mm, kdb_env.man, kdb_ss.man. + + * Released as kdb-v1.5-beta1-2.4.0-test8. + + * Sync to 2.4.0-test9-pre6 and release as kdb-v1.5-beta1-2.4.0-test9-pre6. + +2000-09-23 Keith Owens + + * arch/i386/kdb/kdbasupport.c (kdba_getregcontents): New pseudo + registers cesp and ceflags to help with debugging the debugger. + + * kdb/kdbmain.c (kdb_local, kdb): Add KDB_REASON_RECURSE. Add + environment variable RECURSE. Add code to cope with some types of + recursion. + + * kdb/kdbmain.c (kdb), arch/i386/kdba/kdba_bp.c: Add + kdba_clearsinglestep. + +2000-09-22 Keith Owens + + * drivers/video/vgacon.c (write_vga): No cli() if kdb is running, avoid + console deadlock. + + * arch/i386/kernel/irq.c (get_irqlock): Warn if kdb is running, may hang. + + * include/linux/kdb.h: Define KDB_IS_RUNNING as (0) if no CONFIG_KDB. + + * arch/i386/kdb/kdba_bt.c (kdba_bt_stack): Do not attempt a backtrace if + the code segment is not in the kernel. + + * kdb/modules: Change modules from MX_OBJS to M_OBJS. Remove EXPORT_NOSYMBOLS. + +2000-09-21 Keith Owens + + * arch/i386/kernel/i386_ksyms.c: Move EXPORT_SYMBOLS for kdb to kdb/kdbmain.c. + + * kdb/Makefile: Change kdb/kdbmain.o from O_OBJS to OX_OBJS. + + * arch/i386/kernel/smp.c: Remove some #ifdef CONFIG_KDB. Remove kdbprivate.h. + + * include/linux/kdb.h: Add kdb_print_state. Add KDB_STATE_WAIT_IPI. + + * kdb/kdbmain.c (kdb): Only mark cpu as leaving if it is in KDB state. Maintain + WAIT_IPI state so a cpu is only driven through NMI once. + + * arch/i386/kernel/smp.c (smp_kdb_stop): All state fiddling moved to kdb(). + +2000-09-20 Keith Owens + + * include/linux/kdb.h: #define kdb() as (0) if kdb is not configured. + + * arch/i386/kernel/traps.c: Remove some #ifdef CONFIG_KDB. + + * include/linux/kdbprivate.h: Move per cpu state to kdb.h. + + * include/linux/kdb.h: Add KDB_STATE_NO_WATCHDOG, KDB_STATE_PRINTF_LOCK. + Rename KDB_DEBUG_xxx to KDB_DEBUG_FLAG_xxx. Clean up debug flag + definitions. + + * arch/i386/kernel/traps.c (nmi_watchdog_tick): Check no watchdog. + + * kdb/kdbmain.c (kdb): Set no watchdog in normal kdb code. + + * kdb/kdbmain.c (kdb_parse): Allow watchdog in commands. + + * kdb/kdb_io.c (kdb_printf): No watchdog during printing. Clean up lock handling. + + * kdb/kdbmain.c (kdb_set): Clean up debug flag handling. + +2000-09-19 Juan J. Quintela + + * kdb/arch/i386/kdb/kdba_io.c: Allow kdb to compile without CONFIG_VT and/or + serial console. + +2000-09-19 Keith Owens + + * include/linux/kdb.h: Define KDB_DEBUG_STATE(). + + * kdb/kdbmain.c (kdb): Add kdb_print_state(), calls to KDB_DEBUG_STATE(). + +2000-09-16 Keith Owens + + * Move to finer grained control over individual processors in kdb with + per cpu kdb state. Needed to allow ss[b] to only release one processor, + previously ss[b] released all processors. Also need to recover from + errors inside kdb commands, e.g. oops in kdbm_pg code. + + * various: + Move global flags KDB_FLAG_SSB, KDB_FLAG_SUPRESS, KDB_FLAG_FAULT, + KDB_FLAG_SS, KDB_FLAG_SSBPT, kdb_active, to per cpu state and macros + KDB_STATE(xxx). + Replace kdb_flags & KDB_FLAG_xxx with KDB_FLAG(xxx). + Replace kdb_flags & KDB_DEBUG_xxx with KDB_DEBUG(xxx). + Replace specific tests with wrapper KDB_IS_RUNNING(). + + * various: Remove #ifdef CONFIG_SMP from kdb code wherever + possible. Simplifies the code and makes it much more readable. + + * arch/i386/kdb/kdbasupport.c (kdb_setjmp): Record if we have reliable + longjmp data instead of assuming it is always set. + + * various: Replace smp_kdb_wait with per cpu state, HOLD_CPU. + + * init/main.c : Replace #ifdef KDB_DEBUG with KDB_DEBUG(CALLBACK). + + * include/linux/kdbprivate.h: Separate command return codes from error + codes. Add more detailed command codes. + + * arch/i386/kernel/traps.c (die): Change spin_lock_irq to + spin_lock_irqsave. Why did I do this? + + * kdb/kdbmain.c (kdb_parse): Set per cpu flag CMD before executing kdb + command. More detailed return codes for commands that affect + processors. + + * kdb/kdbmain.c (kdb_previous_event): New, check if any processors are + still executing the previous kdb event. Removes a race window where a + second event could enter kdb before the first had completely ended. + + * kdb/kdbmain.c (kdb): Document all the concurrency conditions and how + kdb handles them. ss[b] now releases only the current cpu. Do not set + breakpoints when releasing for ss[b]. Recover from errors in kdb + commands. Check that we have reliable longjmp data before using it. + + * various: Update return code documentation. + + * kdb/kdb_bp.c (kdb_ss): Separate ss and ssb return codes. + + * kdb/kdbsupport.c (kdb_ipi): Finer grained algorithm for deciding + whether to call send a stop signal to a cpu. + + * arch/i386/kdb/kdba_bp.c (kdba_db_trap): Separate ss and ssb return + codes. Reinstall delayed software breakpoints per cpu instead of + globally. Changed algorithm for handling ss[b]. + + * arch/i386/kdb/kdba_bp.c (kdba_bp_trap): Match software breakpoints per + cpu instead of globally. + + * include/linux/kdb.h: Bump version to kdb v1.5. + +2000-09-16 Keith Owens + + * kernel/sysctl.c (kern_table): add /proc/sys/kernel/kdb. + + * init/main.c (parse_options): add boot flags kdb=on, kdb=off, + kdb=early. + + * include/linux/sysctl.h (enum): add KERN_KDB. + + * drivers/char/serial.c (receive_chars): check kdb_on. + + * drivers/char/keyboard.c (handle_scancode): check kdb_on. + + * arch/i386/kernel/traps.c (nmi_watchdog_tick): check kdb_on. + + * arch/i386/config.in: add CONFIG_KDB_OFF. + + * Documentation/Configure.help: add CONFIG_KDB_OFF. + + * kdb/kdbmain.c: add kdb_initial_cpu, kdb_on. + + * kdb/kdbmain.c (kdb): check kdb_on, set kdb_initial_cpu. + + * kdb/kdbmain.c (kdb_init): add Keith Owens to kdb banner. + + * kdb/kdb_io.c (kdb_printf): serialize kdb_printf output. + + * kdb/kdb_bt.c (kdb_bt): check environment variable BTAPROMPT. + + * kdb/kdbsupport.c (kdb_ipi): ignore NMI for kdb_initial_cpu. + + * kdb/modules/kdbm_pg.c (kdbm_page): merge updates from 2.4.0-test5-xfs. + + * kdb/kdb_bt.man: add btp, bta, BTAPROMPT. + + * kdb/kdb.mm: add CONFIG_KDB_OFF, boot flags, btp, bta. + + * include/linux/kdbprivate.h: add kdb_initial_cpu. + + * include/linux/kdb.h: add kdb_on, bump version to kdb v1.4. diff -urN 2.4.0/kdb/Makefile 2.4.0-ikd-1/kdb/Makefile --- 2.4.0/kdb/Makefile Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kdb/Makefile Mon Jan 15 18:40:05 2001 @@ -0,0 +1,17 @@ +O_TARGET := kdb.o +export-objs := kdbmain.o kdb_io.o +obj-y := kdb_bt.o kdb_bp.o kdb_id.o kdbsupport.o gen-kdb_cmds.o kdbmain.o kdb_io.o + +MOD_SUB_DIRS += modules + +override CFLAGS := $(CFLAGS:%-pg=% ) + +include $(TOPDIR)/Rules.make + +gen-kdb_cmds.c: kdb_cmds Makefile + $(AWK) 'BEGIN {print "#include "} \ + /^ *#/{next} \ + /^[ \t]*$$/{next} \ + {print "static __initdata char kdb_cmd" cmds++ "[] = \"" $$0 "\\n\";"} \ + END {print "char __initdata *kdb_cmds[] = {"; for (i = 0; i < cmds; ++i) {print " kdb_cmd" i ","}; print(" 0\n};");}' \ + kdb_cmds > gen-kdb_cmds.c diff -urN 2.4.0/kdb/kdb_bp.c 2.4.0-ikd-1/kdb/kdb_bp.c --- 2.4.0/kdb/kdb_bp.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kdb/kdb_bp.c Mon Jan 15 18:40:05 2001 @@ -0,0 +1,622 @@ +/* + * Kernel Debugger Breakpoint Handler + * + * Copyright 1999, Silicon Graphics, Inc. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Keith Owens 2000/05/23 + * KDB v1.2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Table of kdb_breakpoints + */ +kdb_bp_t kdb_breakpoints[KDB_MAXBPT]; + +/* + * kdb_bp_install_global + * + * Install global kdb_breakpoints prior to returning from the + * kernel debugger. This allows the kdb_breakpoints to be set + * upon functions that are used internally by kdb, such as + * printk(). + * + * Parameters: + * ef Execution frame. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + * This function is only called once per kdb session. + */ + +void +kdb_bp_install_global(kdb_eframe_t ef) +{ + int i; + + for(i=0; ibp_forcehw) { + kdb_printf("Forced "); + } + + if (!bp->bp_template.bph_free) { + kdb_printf("%s ", kdba_bptype(&bp->bp_template)); + } else { + kdb_printf("Instruction(i) "); + } + + kdb_printf("BP #%d at ", i); + kdb_symbol_print(bp->bp_addr, NULL, KDB_SP_DEFAULT); + + if (bp->bp_enabled) { + kdba_printbp(bp); + if (bp->bp_global) + kdb_printf(" globally"); + else + kdb_printf(" on cpu %d", bp->bp_cpu); + if (bp->bp_adjust) + kdb_printf(" adjust %d", bp->bp_adjust); + } else { + kdb_printf("\n is disabled"); + } + + kdb_printf("\n"); +} + +/* + * kdb_bp + * + * Handle the bp, and bpa commands. + * + * [bp|bpa|bph] [DATAR|DATAW|IO [length]] + * + * Parameters: + * argc Count of arguments in argv + * argv Space delimited command line arguments + * envp Environment value + * regs Exception frame at entry to kernel debugger + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic if failure. + * Locking: + * None. + * Remarks: + * + * bp Set breakpoint. Only use hardware assist if necessary. + * bpa Set breakpoint on all cpus, only use hardware regs if necessary + * bph Set breakpoint - force hardware register + * bpha Set breakpoint on all cpus, force hardware register + */ + +int +kdb_bp(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + kdb_bp_t *bp; + int diag; + int free, same; + kdb_machreg_t addr; + char *symname = NULL; + long offset = 0ul; + int nextarg; + int hardware; + int global; + + if (argc == 0) { + /* + * Display breakpoint table + */ + for(i=0,bp=kdb_breakpoints; ibp_free) continue; + + kdb_printbp(bp, i); + } + + return 0; + } + + global = ((strcmp(argv[0], "bpa") == 0) + || (strcmp(argv[0], "bpha") == 0)); + hardware = ((strcmp(argv[0], "bph") == 0) + || (strcmp(argv[0], "bpha") == 0)); + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, &symname, regs); + if (diag) + return diag; + + /* + * Allocate a new bp structure + */ + free = same = KDB_MAXBPT; + for(i=0,bp=kdb_breakpoints; ibp_free) { + break; + } + } + + if (i == KDB_MAXBPT) + return KDB_TOOMANYBPT; + + bp->bp_addr = addr; + bp->bp_adjust = 0; + + bp->bp_forcehw = hardware; + if (KDB_DEBUG(BP)) + kdb_printf("kdb_bp: forcehw is %d hardware is %d\n", bp->bp_forcehw, hardware); + + /* + * Handle architecture dependent parsing + */ + diag = kdba_parsebp(argc, argv, &nextarg, bp); + if (diag) { + return diag; + } + + bp->bp_enabled = 1; + bp->bp_free = 0; + bp->bp_global = 1; /* Most breakpoints are global */ + + if (hardware && !global) { + bp->bp_global = 0; + bp->bp_cpu = smp_processor_id(); + } + + /* + * Allocate a hardware breakpoint. If one is not available, + * disable the breakpoint, but leave it in the breakpoint + * table. When the breakpoint is re-enabled (via 'be'), we'll + * attempt to allocate a hardware register for it. + */ + if (!bp->bp_template.bph_free) { + bp->bp_hard = kdba_allocbp(&bp->bp_template, &diag); + if (diag) { + bp->bp_enabled = 0; + return diag; + } + bp->bp_hardtype = 1; + } + + kdb_printbp(bp, i); + + return 0; +} + +/* + * kdb_bc + * + * Handles the 'bc', 'be', and 'bd' commands + * + * [bd|bc|be] + * + * Parameters: + * argc Count of arguments in argv + * argv Space delimited command line arguments + * envp Environment value + * regs Exception frame at entry to kernel debugger + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + */ +#define KDBCMD_BC 0 +#define KDBCMD_BE 1 +#define KDBCMD_BD 2 + +int +kdb_bc(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + kdb_machreg_t addr; + kdb_bp_t *bp = 0; + int lowbp = KDB_MAXBPT; + int highbp = 0; + int done = 0; + int i; + int diag; + int cmd; /* KDBCMD_B? */ + + if (strcmp(argv[0], "be") == 0) { + cmd = KDBCMD_BE; + } else if (strcmp(argv[0], "bd") == 0) { + cmd = KDBCMD_BD; + } else + cmd = KDBCMD_BC; + + if (argc != 1) + return KDB_ARGCOUNT; + + if (strcmp(argv[1], "*") == 0) { + lowbp = 0; + highbp = KDB_MAXBPT; + } else { + diag = kdbgetularg(argv[1], &addr); + if (diag) + return diag; + + /* + * For addresses less than the maximum breakpoint number, + * assume that the breakpoint number is desired. + */ + if (addr < KDB_MAXBPT) { + bp = &kdb_breakpoints[addr]; + lowbp = highbp = addr; + highbp++; + } else { + for(i=0, bp=kdb_breakpoints; ibp_addr == addr) { + lowbp = highbp = i; + highbp++; + break; + } + } + } + } + + /* + * Now operate on the set of breakpoints matching the input + * criteria (either '*' for all, or an individual breakpoint). + */ + for(bp=&kdb_breakpoints[lowbp], i=lowbp; + i < highbp; + i++, bp++) { + if (bp->bp_free) + continue; + + done++; + + switch (cmd) { + case KDBCMD_BC: + if (bp->bp_hardtype) { + kdba_freebp(bp->bp_hard); + bp->bp_hard = 0; + bp->bp_hardtype = 0; + } + + bp->bp_enabled = 0; + bp->bp_global = 0; + + kdb_printf("Breakpoint %d at " kdb_bfd_vma_fmt " cleared\n", + i, bp->bp_addr); + + bp->bp_addr = 0; + bp->bp_free = 1; + + break; + case KDBCMD_BE: + /* + * Allocate a hardware breakpoint. If one is not + * available, don't enable the breakpoint. + */ + if (!bp->bp_template.bph_free + && !bp->bp_hardtype) { + bp->bp_hard = kdba_allocbp(&bp->bp_template, &diag); + if (diag) { + bp->bp_enabled = 0; + return diag; + } + bp->bp_hardtype = 1; + } + + bp->bp_enabled = 1; + + kdb_printf("Breakpoint %d at " kdb_bfd_vma_fmt " in enabled", + i, bp->bp_addr); + + kdb_printf("\n"); + break; + case KDBCMD_BD: + if (!bp->bp_enabled) { + return 0; + } + + /* + * Since this breakpoint is now disabled, we can + * give up the hardware register which is allocated + * to it. + */ + if (bp->bp_hardtype) { + kdba_freebp(bp->bp_hard); + bp->bp_hard = 0; + bp->bp_hardtype = 0; + } + + bp->bp_enabled = 0; + + kdb_printf("Breakpoint %d at " kdb_bfd_vma_fmt " disabled\n", + i, bp->bp_addr); + + break; + } + } + + return (!done)?KDB_BPTNOTFOUND:0; +} +/* + * kdb_ss + * + * Process the 'ss' (Single Step) and 'ssb' (Single Step to Branch) + * commands. + * + * ss [] + * ssb + * + * Parameters: + * argc Argument count + * argv Argument vector + * envp Environment vector + * regs Registers at time of entry to kernel debugger + * Outputs: + * None. + * Returns: + * KDB_CMD_SS[B] for success, a kdb error if failure. + * Locking: + * None. + * Remarks: + * + * Set the trace flag in the EFLAGS register to trigger + * a debug trap after the next instruction. Print the + * current instruction. + * + * For 'ssb', set the trace flag in the debug trap handler + * after printing the current insn and return directly without + * invoking the kdb command processor, until a branch instruction + * is encountered or SSCOUNT lines are printed. + */ + +int +kdb_ss(int argc, const char **argv, const char **envp, kdb_eframe_t ef) +{ + int ssb = 0; + + ssb = (strcmp(argv[0], "ssb") == 0); + if ((ssb && (argc != 0)) + || (!ssb && (argc > 1))) { + return KDB_ARGCOUNT; + } + +#if 0 + /* + * Fetch provided count + */ + diag = kdbgetularg(argv[1], &sscount); + if (diag) + return diag; +#endif + + /* + * Print current insn + */ + kdb_id1(kdba_getpc(ef)); + + /* + * Set trace flag and go. + */ + KDB_STATE_SET(DOING_SS); + if (ssb) + KDB_STATE_SET(DOING_SSB); + + kdba_setsinglestep(ef); /* Enable single step */ + + if (ssb) + return KDB_CMD_SSB; + return KDB_CMD_SS; +} + +/* + * kdb_initbptab + * + * Initialize the breakpoint table. Register breakpoint commands. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void __init +kdb_initbptab(void) +{ + int i; + kdb_bp_t *bp; + + /* + * First time initialization. + */ + memset(&kdb_breakpoints, '\0', sizeof(kdb_breakpoints)); + + for (i=0, bp=kdb_breakpoints; ibp_free = 1; + /* + * The bph_free flag is architecturally required. It + * is set by architecture-dependent code to false (zero) + * in the event a hardware breakpoint register is required + * for this breakpoint. + * + * The rest of the template is reserved to the architecture + * dependent code and _must_ not be touched by the architecture + * independent code. + */ + bp->bp_template.bph_free = 1; + } + + kdb_register("bp", kdb_bp, "[]", "Set/Display breakpoints", 0); + kdb_register("bl", kdb_bp, "[]", "Display breakpoints", 0); + kdb_register("bpa", kdb_bp, "[]", "Set/Display global breakpoints", 0); + kdb_register("bph", kdb_bp, "[]", "Set hardware breakpoint", 0); + kdb_register("bpha", kdb_bp, "[]", "Set global hardware breakpoint", 0); + kdb_register("bc", kdb_bc, "", "Clear Breakpoint", 0); + kdb_register("be", kdb_bc, "", "Enable Breakpoint", 0); + kdb_register("bd", kdb_bc, "", "Disable Breakpoint", 0); + + kdb_register("ss", kdb_ss, "[<#steps>]", "Single Step", 1); + kdb_register("ssb", kdb_ss, "", "Single step to branch/call", 0); + /* + * Architecture dependent initialization. + */ + kdba_initbp(); +} + diff -urN 2.4.0/kdb/kdb_bt.c 2.4.0-ikd-1/kdb/kdb_bt.c --- 2.4.0/kdb/kdb_bt.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kdb/kdb_bt.c Mon Jan 15 18:40:05 2001 @@ -0,0 +1,138 @@ +/* + * Minimalist Kernel Debugger - Architecture independent stack traceback + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + * Masahiro Adegawa 1999/12/01 + * 'sr' command, active flag in 'ps' + * Scott Lurndal 1999/12/12 + * Significantly restructure for linux2.3 + * Keith Owens 2000/05/23 + * KDB v1.2 + * Keith Owens 2000/09/16 + * KDB v1.4 + * Env BTAPROMPT. + * + */ + +#include +#include +#include +#include +#include +#include +#include + + +/* + * kdb_bt + * + * This function implements the 'bt' command. Print a stack + * traceback. + * + * bt [] (addr-exp is for alternate stacks) + * btp (Kernel stack for ) + * + * address expression refers to a return address on the stack. It + * is expected to be preceeded by a frame pointer. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * ef registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Backtrack works best when the code uses frame pointers. But + * even without frame pointers we should get a reasonable trace. + * + * mds comes in handy when examining the stack to do a manual + * traceback. + */ + +int +kdb_bt(int argc, const char **argv, const char **envp, kdb_eframe_t ef) +{ + int diag; + int argcount = 5; + int btaprompt = 1; + char buffer[80]; + int nextarg; + unsigned long addr; + long offset; + + kdbgetintenv("BTARGS", &argcount); /* Arguments to print */ + kdbgetintenv("BTAPROMPT", &btaprompt); /* Prompt after each proc in bta */ + + if (strcmp(argv[0], "bta") == 0) { + struct task_struct *p; + + for_each_task(p) { + kdb_printf("Stack traceback for pid %d\n", p->pid); + + diag = kdba_bt_process(p, argcount); + + if (btaprompt) { + kdb_getstr(buffer, sizeof(buffer), + "Enter to end, to continue:"); + + if (buffer[0] == 'q') { + return 0; + } + } + } + } else if (strcmp(argv[0], "btp") == 0) { + struct task_struct *p; + int pid; + + if (argc < 1) + return KDB_ARGCOUNT; + + diag = kdbgetularg((char *)argv[1], (unsigned long*)&pid); + if (diag) + return diag; + + for_each_task(p) { + if (p->pid == pid) { + return kdba_bt_process(p, argcount); + } + } + + kdb_printf("No process with pid == %d found\n", pid); + return 0; + } else { + if (argc) { + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, + &offset, NULL, ef); + if (diag) + return diag; + + return kdba_bt_stack(ef, &addr, argcount, current); + } else { + return kdba_bt_stack(ef, NULL, argcount, current); + } + } + + /* NOTREACHED */ + return 0; +} diff -urN 2.4.0/kdb/kdb_cmds 2.4.0-ikd-1/kdb/kdb_cmds --- 2.4.0/kdb/kdb_cmds Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kdb/kdb_cmds Mon Jan 15 18:40:05 2001 @@ -0,0 +1,6 @@ +# Initial commands for kdb, alter to suit your needs. +# These commands are executed in kdb_init() context, no SMP, no +# processes. Commands that require process data (including stack or +# registers) are not reliable this early. set and bp commands should +# be safe. Global breakpoint commands affect each cpu as it is booted. + diff -urN 2.4.0/kdb/kdb_id.c 2.4.0-ikd-1/kdb/kdb_id.c --- 2.4.0/kdb/kdb_id.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kdb/kdb_id.c Mon Jan 15 18:40:05 2001 @@ -0,0 +1,253 @@ +/* + * Minimalist Kernel Debugger - Architecture Independent Instruction Disassembly + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + * Masahiro Adegawa 1999/12/01 + * 'sr' command, active flag in 'ps' + * Scott Lurndal 1999/12/12 + * Significantly restructure for linux2.3 + * Keith Owens 2000/05/23 + * KDB v1.2 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +disassemble_info kdb_di; + +/* + * kdb_id + * + * Handle the id (instruction display) command. + * + * id [] + * + * Parameters: + * argc Count of arguments in argv + * argv Space delimited command line arguments + * envp Environment value + * regs Exception frame at entry to kernel debugger + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic if failure. + * Locking: + * None. + * Remarks: + */ + +int +kdb_id(int argc, const char **argv, const char **envp, struct pt_regs* regs) +{ + kdb_machreg_t pc; + int icount; + int diag; + int i; + char * mode; + int nextarg; + long offset = 0; + static kdb_machreg_t lastpc; + struct disassemble_info *dip = &kdb_di; + + if (argc != 1) { + if (lastpc == 0) { + return KDB_ARGCOUNT; + } else { + char lastbuf[50]; + sprintf(lastbuf, "0x%lx", lastpc); + argv[1] = lastbuf; + argc = 1; + } + } + + + /* + * Fetch PC. First, check to see if it is a symbol, if not, + * try address. + */ + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &pc, &offset, NULL, regs); + if (diag) + return diag; + kdba_check_pc(&pc); + + /* + * Number of lines to display + */ + diag = kdbgetintenv("IDCOUNT", &icount); + if (diag) + return diag; + + mode = kdbgetenv("IDMODE"); + diag = kdba_id_parsemode(mode, dip); + if (diag) { + return diag; + } + + for(i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static struct console *kdbcons; + +/* + * kdb_read + * + * This function reads a string of characters, terminated by + * a newline, or by reaching the end of the supplied buffer, + * from the current kernel debugger console device. + * Parameters: + * buffer - Address of character buffer to receive input characters. + * bufsize - size, in bytes, of the character buffer + * Returns: + * Returns a pointer to the buffer containing the received + * character string. This string will be terminated by a + * newline character. + * Locking: + * No locks are required to be held upon entry to this + * function. It is not reentrant - it relies on the fact + * that while kdb is running on any one processor all other + * processors will be spinning at the kdb barrier. + * Remarks: + * + * Davidm asks, why doesn't kdb use the console abstraction; + * here are some reasons: + * - you cannot debug the console abstraction with kdb if + * kdb uses it. + * - you rely on the correct functioning of the abstraction + * in the presence of general system failures. + * - You must acquire the console spinlock thus restricting + * the usability - what if the kernel fails with the spinlock + * held - one still wishes to debug such situations. + * - How about debugging before the console(s) are registered? + * - None of the current consoles (sercons, vt_console_driver) + * have read functions defined. + * - The standard pc keyboard and terminal drivers are interrupt + * driven. We cannot enable interrupts while kdb is active, + * so the standard input functions cannot be used by kdb. + * + * An implementation could be improved by removing the need for + * lock acquisition - just keep a 'struct console *kdbconsole;' global + * variable which refers to the preferred kdb console. + * + * The bulk of this function is architecture dependent. + */ + +char * +kdb_read(char *buffer, size_t bufsize) +{ + int no_watchdog; + char *ret; + no_watchdog = KDB_STATE(NO_WATCHDOG); + KDB_STATE_SET(NO_WATCHDOG); + ret = kdba_read(buffer, bufsize); + if (!no_watchdog) + KDB_STATE_CLEAR(NO_WATCHDOG); + return(ret); +} + +/* + * kdb_getstr + * + * Print the prompt string and read a command from the + * input device. + * + * Parameters: + * buffer Address of buffer to receive command + * bufsize Size of buffer in bytes + * prompt Pointer to string to use as prompt string + * Returns: + * Pointer to command buffer. + * Locking: + * None. + * Remarks: + * For SMP kernels, the processor number will be + * substituted for %d, %x or %o in the prompt. + */ + +char * +kdb_getstr(char *buffer, size_t bufsize, char *prompt) +{ +#if defined(CONFIG_SMP) + kdb_printf(prompt, smp_processor_id()); +#else + kdb_printf("%s", prompt); +#endif + kdb_nextline = 1; /* Prompt and input resets line number */ + return kdb_read(buffer, bufsize); +} + +/* + * kdb_printf + * + * Print a string to the output device(s). + * + * Parameters: + * printf-like format and optional args. + * Returns: + * 0 + * Locking: + * None. + * Remarks: + * use 'kdbcons->write()' to avoid polluting 'log_buf' with + * kdb output. + */ + +void +kdb_printf(const char *fmt, ...) +{ + char buffer[256]; + va_list ap; + int diag; + int linecount; + int logging, saved_loglevel = 0; + int no_watchdog, do_longjmp = 0; + struct console *c = console_drivers; + static spinlock_t kdb_printf_lock = SPIN_LOCK_UNLOCKED; + + /* Output is slow, especially over serial lines or while waiting + * for user response. No watchdogs wanted. + */ + no_watchdog = KDB_STATE(NO_WATCHDOG); + KDB_STATE_SET(NO_WATCHDOG); + + /* Serialize kdb_printf if multiple cpus try to write at once. + * But if any cpu goes recursive in kdb, just print the output, + * even if it is interleaved with any other text. + */ + if (!KDB_STATE(PRINTF_LOCK)) { + KDB_STATE_SET(PRINTF_LOCK); + spin_lock(&kdb_printf_lock); + } + + diag = kdbgetintenv("LINES", &linecount); + if (diag) + linecount = 22; + + diag = kdbgetintenv("LOGGING", &logging); + if (diag) + logging = 0; + + va_start(ap, fmt); + vsprintf(buffer, fmt, ap); + va_end(ap); + + /* + * Write to all consoles. + */ + while (c) { + c->write(c, buffer, strlen(buffer)); + c = c->next; + } + if (logging) { + spin_lock_irq(&console_lock); + saved_loglevel = console_loglevel; + console_loglevel = 0; + spin_unlock_irq(&console_lock); + printk("%s", buffer); + } + + if (strchr(buffer, '\n') != NULL) { + kdb_nextline++; + } + + if (kdb_nextline == linecount) { +#ifdef KDB_HAVE_LONGJMP + char buf1[16]; +#if defined(CONFIG_SMP) + char buf2[32]; +#endif + char *moreprompt; + + /* Watch out for recursion here. Any routine that calls + * kdb_printf will come back through here. And kdb_read + * uses kdb_printf to echo on serial consoles ... + */ + kdb_nextline = 1; /* In case of recursion */ + + /* + * Pause until cr. + */ + moreprompt = kdbgetenv("MOREPROMPT"); + if (moreprompt == NULL) { + moreprompt = "more> "; + } + +#if defined(CONFIG_SMP) + if (strchr(moreprompt, '%')) { + sprintf(buf2, moreprompt, smp_processor_id()); + moreprompt = buf2; + } +#endif + + c = console_drivers; + while (c) { + c->write(c, moreprompt, strlen(moreprompt)); + c = c->next; + } + if (logging) + printk("%s", moreprompt); + + kdb_read(buf1, sizeof(buf1)); + kdb_nextline = 1; /* Really set output line 1 */ + + if ((buf1[0] == 'q') + || (buf1[0] == 'Q')) { + do_longjmp = 1; + } +#endif /* KDB_HAVE_LONGJMP */ + } + + if (logging) { + spin_lock_irq(&console_lock); + console_loglevel = saved_loglevel; + spin_unlock_irq(&console_lock); + } + if (!no_watchdog) + KDB_STATE_CLEAR(NO_WATCHDOG); + if (KDB_STATE(PRINTF_LOCK)) { + spin_unlock(&kdb_printf_lock); + KDB_STATE_CLEAR(PRINTF_LOCK); + } + if (do_longjmp) +#ifdef KDB_HAVE_LONGJMP + kdb_longjmp(&kdbjmpbuf[smp_processor_id()], 1); +#else + ; +#endif /* KDB_HAVE_LONGJMP */ +} + +/* + * kdb_io_init + * + * Initialize kernel debugger output environment. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * Select a console device. + */ + +void __init +kdb_io_init(void) +{ + /* + * Select a console. + */ + struct console *c = console_drivers; + + while (c) { + if ((c->flags & CON_CONSDEV)) { + kdbcons = c; + break; + } + c = c->next; + } + + if (kdbcons == NULL) { + long long i; + + printk("kdb: Initialization failed - no console\n"); + while (1) i++; + } + return; +} + +EXPORT_SYMBOL(kdb_read); diff -urN 2.4.0/kdb/kdbmain.c 2.4.0-ikd-1/kdb/kdbmain.c --- 2.4.0/kdb/kdbmain.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kdb/kdbmain.c Mon Jan 15 18:40:05 2001 @@ -0,0 +1,2737 @@ +/* + * Minimalist Kernel Debugger + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * Copyright (C) 2000 Stephane Eranian + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + * Masahiro Adegawa 1999/12/01 + * 'sr' command, active flag in 'ps' + * Scott Lurndal 1999/12/12 + * Significantly restructure for linux2.3 + * Keith Owens 2000/05/23 + * KDB v1.2 + * Keith Owens 2000/06/09 + * KDB v1.3. + * Rewrite SMP handling. + * Add NMI watchdog from Ted Kline, + * lsmod/rmmod commands from Marc Esipovich + * Stephane Eranian 2000/06/05 + * Enabled disassembler support. Added command history support. + * + * Keith Owens 2000/09/16 + * KDB v1.4 + * kdb=on/off/early at boot, /proc/sys/kernel/kdb. + * Env BTAPROMPT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(CONFIG_MODULES) +extern struct module *module_list; +#endif + + /* + * Kernel debugger state flags + */ +volatile int kdb_flags; + + /* + * kdb_lock protects updates to kdb_initial_cpu. Used to + * single thread processors through the kernel debugger. + */ +spinlock_t kdb_lock = SPIN_LOCK_UNLOCKED; +volatile int kdb_initial_cpu = -1; /* cpu number that owns kdb */ + +volatile int kdb_nextline = 1; +static volatile int kdb_new_cpu; /* Which cpu to switch to */ + +volatile int kdb_state[NR_CPUS]; /* Per cpu state */ + +#ifdef CONFIG_KDB_OFF +int kdb_on = 0; /* Default is off */ +#else +int kdb_on = 1; /* Default is on */ +#endif /* CONFIG_KDB_OFF */ + +#ifdef KDB_HAVE_LONGJMP + /* + * Must have a setjmp buffer per CPU. Switching cpus will + * cause the jump buffer to be setup for the new cpu, and + * subsequent switches (and pager aborts) will use the + * appropriate per-processor values. + */ +kdb_jmp_buf kdbjmpbuf[NR_CPUS]; +#endif /* KDB_HAVE_LONGJMP */ + + /* + * kdb_commands describes the available commands. + */ +static kdbtab_t kdb_commands[KDB_MAX_COMMANDS]; + +typedef struct _kdbmsg { + int km_diag; /* kdb diagnostic */ + char *km_msg; /* Corresponding message text */ +} kdbmsg_t; + +#define KDBMSG(msgnum, text) \ + { KDB_##msgnum, text } + +static kdbmsg_t kdbmsgs[] = { + KDBMSG(NOTFOUND,"Command Not Found"), + KDBMSG(ARGCOUNT, "Improper argument count, see usage."), + KDBMSG(BADWIDTH, "Illegal value for BYTESPERWORD use 1, 2 or 4"), + KDBMSG(BADRADIX, "Illegal value for RADIX use 8, 10 or 16"), + KDBMSG(NOTENV, "Cannot find environment variable"), + KDBMSG(NOENVVALUE, "Environment variable should have value"), + KDBMSG(NOTIMP, "Command not implemented"), + KDBMSG(ENVFULL, "Environment full"), + KDBMSG(ENVBUFFULL, "Environment buffer full"), + KDBMSG(TOOMANYBPT, "Too many breakpoints defined"), + KDBMSG(TOOMANYDBREGS, "More breakpoints than db registers defined"), + KDBMSG(DUPBPT, "Duplicate breakpoint address"), + KDBMSG(BPTNOTFOUND, "Breakpoint not found"), + KDBMSG(BADMODE, "Invalid IDMODE"), + KDBMSG(BADINT, "Illegal numeric value"), + KDBMSG(INVADDRFMT, "Invalid symbolic address format"), + KDBMSG(BADREG, "Invalid register name"), + KDBMSG(BADCPUNUM, "Invalid cpu number"), + KDBMSG(BADLENGTH, "Invalid length field"), + KDBMSG(NOBP, "No Breakpoint exists"), +}; +#undef KDBMSG + +static const int __nkdb_err = sizeof(kdbmsgs) / sizeof(kdbmsg_t); + + +/* + * Initial environment. This is all kept static and local to + * this file. We don't want to rely on the memory allocation + * mechanisms in the kernel, so we use a very limited allocate-only + * heap for new and altered environment variables. The entire + * environment is limited to a fixed number of entries (add more + * to __env[] if required) and a fixed amount of heap (add more to + * KDB_ENVBUFSIZE if required). + */ + +static char *__env[] = { +#if defined(CONFIG_SMP) + "PROMPT=[%d]kdb> ", + "MOREPROMPT=[%d]more> ", +#else + "PROMPT=kdb> ", + "MOREPROMPT=more> ", +#endif + "RADIX=16", + "LINES=24", + "COLUMNS=80", + "MDCOUNT=8", /* lines of md output */ + "BTARGS=5", /* 5 possible args in bt */ + "SSCOUNT=20", /* lines of ssb output */ + KDB_PLATFORM_ENV, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, +}; + +static const int __nenv = (sizeof(__env) / sizeof(char *)); + +/* + * kdbgetenv + * + * This function will return the character string value of + * an environment variable. + * + * Parameters: + * match A character string representing an environment variable. + * Outputs: + * None. + * Returns: + * NULL No environment variable matches 'match' + * char* Pointer to string value of environment variable. + * Locking: + * No locking considerations required. + * Remarks: + */ +char * +kdbgetenv(const char *match) +{ + char **ep = __env; + int matchlen = strlen(match); + int i; + + for(i=0; i<__nenv; i++) { + char *e = *ep++; + + if (!e) continue; + + if ((strncmp(match, e, matchlen) == 0) + && ((e[matchlen] == '\0') + ||(e[matchlen] == '='))) { + char *cp = strchr(e, '='); + return (cp)?++cp:""; + } + } + return (char *)0; +} + +/* + * kdballocenv + * + * This function is used to allocate bytes for environment entries. + * + * Parameters: + * match A character string representing a numeric value + * Outputs: + * *value the unsigned long represntation of the env variable 'match' + * Returns: + * Zero on success, a kdb diagnostic on failure. + * Locking: + * No locking considerations required. Must be called with all + * processors halted. + * Remarks: + * We use a static environment buffer (envbuffer) to hold the values + * of dynamically generated environment variables (see kdb_set). Buffer + * space once allocated is never free'd, so over time, the amount of space + * (currently 512 bytes) will be exhausted if env variables are changed + * frequently. + */ +static char * +kdballocenv(size_t bytes) +{ +#define KDB_ENVBUFSIZE 512 + static char envbuffer[KDB_ENVBUFSIZE]; + static int envbufsize; + char *ep = (char *)0; + + if ((KDB_ENVBUFSIZE - envbufsize) >= bytes) { + ep = &envbuffer[envbufsize]; + envbufsize += bytes; + } + return ep; +} + +/* + * kdbgetulenv + * + * This function will return the value of an unsigned long-valued + * environment variable. + * + * Parameters: + * match A character string representing a numeric value + * Outputs: + * *value the unsigned long represntation of the env variable 'match' + * Returns: + * Zero on success, a kdb diagnostic on failure. + * Locking: + * No locking considerations required. + * Remarks: + */ + +int +kdbgetulenv(const char *match, unsigned long *value) +{ + char *ep; + + ep = kdbgetenv(match); + if (!ep) return KDB_NOTENV; + if (strlen(ep) == 0) return KDB_NOENVVALUE; + + *value = simple_strtoul(ep, 0, 0); + + return 0; +} + +/* + * kdbgetintenv + * + * This function will return the value of an integer-valued + * environment variable. + * + * Parameters: + * match A character string representing an integer-valued env variable + * Outputs: + * *value the integer representation of the environment variable 'match' + * Returns: + * Zero on success, a kdb diagnostic on failure. + * Locking: + * No locking considerations required. + * Remarks: + */ + +int +kdbgetintenv(const char *match, int *value) { + unsigned long val; + int diag; + + diag = kdbgetulenv(match, &val); + if (!diag) { + *value = (int) val; + } + return diag; +} + +/* + * kdbgetularg + * + * This function will convert a numeric string + * into an unsigned long value. + * + * Parameters: + * arg A character string representing a numeric value + * Outputs: + * *value the unsigned long represntation of arg. + * Returns: + * Zero on success, a kdb diagnostic on failure. + * Locking: + * No locking considerations required. + * Remarks: + */ + +int +kdbgetularg(const char *arg, unsigned long *value) +{ + char *endp; + unsigned long val; + + val = simple_strtoul(arg, &endp, 0); + + if (endp == arg) { + /* + * Try base 16, for us folks too lazy to type the + * leading 0x... + */ + val = simple_strtoul(arg, &endp, 16); + if (endp == arg) + return KDB_BADINT; + } + + *value = val; + + return 0; +} + +/* + * kdbgetaddrarg + * + * This function is responsible for parsing an + * address-expression and returning the value of + * the expression, symbol name, and offset to the caller. + * + * The argument may consist of a numeric value (decimal or + * hexidecimal), a symbol name, a register name (preceeded + * by the percent sign), an environment variable with a numeric + * value (preceeded by a dollar sign) or a simple arithmetic + * expression consisting of a symbol name, +/-, and a numeric + * constant value (offset). + * + * Parameters: + * argc - count of arguments in argv + * argv - argument vector + * *nextarg - index to next unparsed argument in argv[] + * regs - Register state at time of KDB entry + * Outputs: + * *value - receives the value of the address-expression + * *offset - receives the offset specified, if any + * *name - receives the symbol name, if any + * *nextarg - index to next unparsed argument in argv[] + * + * Returns: + * zero is returned on success, a kdb diagnostic code is + * returned on error. + * + * Locking: + * No locking requirements. + * + * Remarks: + * + */ + +int +kdbgetaddrarg(int argc, const char **argv, int *nextarg, + kdb_machreg_t *value, long *offset, + char **name, kdb_eframe_t ef) +{ + kdb_machreg_t addr; + long off = 0; + int positive; + int diag; + int found = 0; + char *symname; + char symbol = '\0'; + char *cp; + kdb_symtab_t symtab; + + /* + * Process arguments which follow the following syntax: + * + * symbol | numeric-address [+/- numeric-offset] + * %register + * $environment-variable + */ + + if (*nextarg > argc) { + return KDB_ARGCOUNT; + } + + symname = (char *)argv[*nextarg]; + + /* + * If there is no whitespace between the symbol + * or address and the '+' or '-' symbols, we + * remember the character and replace it with a + * null so the symbol/value can be properly parsed + */ + if ((cp = strpbrk(symname, "+-")) != NULL) { + symbol = *cp; + *cp++ = '\0'; + } + + if (symname[0] == '$') { + diag = kdbgetulenv(&symname[1], &addr); + if (diag) + return diag; + } else if (symname[0] == '%') { + diag = kdba_getregcontents(&symname[1], ef, &addr); + if (diag) + return diag; + } else { + found = kdbgetsymval(symname, &symtab); + if (found) { + addr = symtab.sym_start; + } else { + diag = kdbgetularg(argv[*nextarg], &addr); + if (diag) + return diag; + } + } + + if (!found) + found = kdbnearsym(addr, &symtab); + + (*nextarg)++; + + if (name) + *name = symname; + if (value) + *value = addr; + if (offset && name && *name) + *offset = addr - symtab.sym_start; + + if ((*nextarg > argc) + && (symbol == '\0')) + return 0; + + /* + * check for +/- and offset + */ + + if (symbol == '\0') { + if ((argv[*nextarg][0] != '+') + && (argv[*nextarg][0] != '-')) { + /* + * Not our argument. Return. + */ + return 0; + } else { + positive = (argv[*nextarg][0] == '+'); + (*nextarg)++; + } + } else + positive = (symbol == '+'); + + /* + * Now there must be an offset! + */ + if ((*nextarg > argc) + && (symbol == '\0')) { + return KDB_INVADDRFMT; + } + + if (!symbol) { + cp = (char *)argv[*nextarg]; + (*nextarg)++; + } + + diag = kdbgetularg(cp, &off); + if (diag) + return diag; + + if (!positive) + off = -off; + + if (offset) + *offset += off; + + if (value) + *value += off; + + return 0; +} + +static void +kdb_cmderror(int diag) +{ + int i; + + if (diag >= 0) { + kdb_printf("no error detected\n"); + return; + } + + for(i=0; i<__nkdb_err; i++) { + if (kdbmsgs[i].km_diag == diag) { + kdb_printf("diag: %d: %s\n", diag, kdbmsgs[i].km_msg); + return; + } + } + + kdb_printf("Unknown diag %d\n", -diag); +} + +/* + * kdb_parse + * + * Parse the command line, search the command table for a + * matching command and invoke the command function. + * + * Parameters: + * cmdstr The input command line to be parsed. + * regs The registers at the time kdb was entered. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic if failure. + * Locking: + * None. + * Remarks: + * Limited to 20 tokens. + * + * Real rudimentary tokenization. Basically only whitespace + * is considered a token delimeter (but special consideration + * is taken of the '=' sign as used by the 'set' command). + * + * The algorithm used to tokenize the input string relies on + * there being at least one whitespace (or otherwise useless) + * character between tokens as the character immediately following + * the token is altered in-place to a null-byte to terminate the + * token string. + */ + +#define MAXARGC 20 + +static int +kdb_parse(char *cmdstr, kdb_eframe_t ef) +{ + char *argv[MAXARGC]; + int argc=0; + char *cp; + kdbtab_t *tp; + int i; + + /* + * First tokenize the command string. + */ + cp = cmdstr; + + /* + * If a null statement is provided, do nothing. + */ + if ((*cp == '\n') || (*cp == '\0')) + return 0; + + while (*cp) { + /* skip whitespace */ + while (isspace(*cp)) cp++; + if ((*cp == '\0') || (*cp == '\n')) + break; + argv[argc++] = cp; + /* Skip to next whitespace */ + for(; *cp && (!isspace(*cp) && (*cp != '=')); cp++); + *cp++ = '\0'; /* Squash a ws or '=' character */ + } + if (!argc) + return 0; + + for(tp=kdb_commands, i=0; i < KDB_MAX_COMMANDS; i++,tp++) { + if (tp->cmd_name) { + /* + * If this command is allowed to be abbreviated, + * check to see if this is it. + */ + + if (tp->cmd_minlen + && (strlen(argv[0]) <= tp->cmd_minlen)) { + if (strncmp(argv[0], + tp->cmd_name, + tp->cmd_minlen) == 0) { + break; + } + } + + if (strcmp(argv[0], tp->cmd_name)==0) { + break; + } + } + } + + if (i < KDB_MAX_COMMANDS) { + int result, no_watchdog; + KDB_STATE_SET(CMD); + no_watchdog = KDB_STATE(NO_WATCHDOG); + KDB_STATE_CLEAR(NO_WATCHDOG); + result = (*tp->cmd_func)(argc-1, + (const char**)argv, + (const char**)__env, + ef); + if (no_watchdog) + KDB_STATE_SET(NO_WATCHDOG); + KDB_STATE_CLEAR(CMD); + return result; + } + + /* + * If the input with which we were presented does not + * map to an existing command, attempt to parse it as an + * address argument and display the result. Useful for + * obtaining the address of a variable, or the nearest symbol + * to an address contained in a register. + */ + { + kdb_machreg_t value; + char *name = NULL; + long offset; + int nextarg = 0; + + if (kdbgetaddrarg(0, (const char **)argv, &nextarg, + &value, &offset, &name, ef)) { + return KDB_NOTFOUND; + } + + kdb_printf("%s = ", argv[0]); + kdb_symbol_print(value, NULL, KDB_SP_DEFAULT); + kdb_printf("\n"); + return 0; + } +} + +/* The command history feature is not functional at the moment. It + * will be replaced by something that understands editting keys, + * including left, right, insert, delete as well as up, down. + * Keith Owens, November 18 2000 + */ +#define KDB_CMD_HISTORY_COUNT 32 +#define CMD_BUFLEN 200 /* kdb_printf: max printline size == 256 */ +static unsigned int cmd_head, cmd_tail; +static unsigned int cmdptr; +static char cmd_hist[KDB_CMD_HISTORY_COUNT][CMD_BUFLEN]; + + +static int +handle_ctrl_cmd(char *cmd) +{ +#define CTRL_P 16 +#define CTRL_N 14 + + /* initial situation */ + if (cmd_head == cmd_tail) return 1; + + switch(*cmd) { + case '\n': + case CTRL_P: + if (cmdptr != cmd_tail) + cmdptr = (cmdptr-1) % KDB_CMD_HISTORY_COUNT; + strcpy(cmd, cmd_hist[cmdptr]); + return 0; + case CTRL_N: + if (cmdptr != (cmd_head-1)) + cmdptr = (cmdptr+1) % KDB_CMD_HISTORY_COUNT; + strcpy(cmd, cmd_hist[cmdptr]); + return 0; + } + return 1; +} + + +/* + * kdb_local + * + * The main code for kdb. This routine is invoked on a specific + * processor, it is not global. The main kdb() routine ensures + * that only one processor at a time is in this routine. This + * code is called with the real reason code on the first entry + * to a kdb session, thereafter it is called with reason SWITCH, + * even if the user goes back to the original cpu. + * + * Inputs: + * reason The reason KDB was invoked + * error The hardware-defined error code + * ef The exception frame at time of fault/breakpoint. NULL + * for reason SILENT, otherwise valid. + * db_result Result code from the break or debug point. + * Returns: + * 0 KDB was invoked for an event which it wasn't responsible + * 1 KDB handled the event for which it was invoked. + * KDB_CMD_GO User typed 'go'. + * KDB_CMD_CPU User switched to another cpu. + * KDB_CMD_SS Single step. + * KDB_CMD_SSB Single step until branch. + * Locking: + * none + * Remarks: + * none + */ + +static int +kdb_local(kdb_reason_t reason, int error, kdb_eframe_t ef, kdb_dbtrap_t db_result) +{ + char *cmdbuf; + char cmd[CMD_BUFLEN]; + int diag; + int parsed_once=0; /* if falsem don't repeat last cmd on CR */ + + if (reason != KDB_REASON_DEBUG && + reason != KDB_REASON_SILENT) { + kdb_printf("\nEntering kdb (current=0x%p, pid %d) ", (void *)current, current->pid); +#if defined(CONFIG_SMP) + kdb_printf("on processor %d ", smp_processor_id()); +#endif + } + + switch (reason) { + case KDB_REASON_DEBUG: + case KDB_REASON_FLTDBG: + { + /* + * If re-entering kdb after a single step + * command, don't print the message. + */ + switch(db_result) { + case KDB_DB_BPT: + kdb_printf("\nEntering kdb (0x%p) ", (void *)current); +#if defined(CONFIG_SMP) + kdb_printf("on processor %d ", smp_processor_id()); +#endif + kdb_printf("due to Debug @ " kdb_machreg_fmt "\n", kdba_getpc(ef)); + break; + case KDB_DB_SSB: + /* + * In the midst of ssb command. Just return. + */ + return KDB_CMD_SSB; /* Continue with SSB command */ + + break; + case KDB_DB_SS: + break; + case KDB_DB_SSBPT: + return 1; /* kdba_db_trap did the work */ + default: + kdb_printf("kdb: Bad result from kdba_db_trap: %d\n", + db_result); + break; + } + + } + break; + case KDB_REASON_FAULT: + break; + case KDB_REASON_ENTER: + kdb_printf("due to KDB_ENTER()\n"); + break; + case KDB_REASON_KEYBOARD: + kdb_printf("due to Keyboard Entry\n"); + break; + case KDB_REASON_SWITCH: + kdb_printf("due to cpu switch\n"); + break; + case KDB_REASON_CALL: + kdb_printf("due to direct function call\n"); + /* + * Get a set of registers that defines the current + * context (as of the call to kdb). + */ + kdb_getcurrentframe(ef); + kdba_setpc(ef, (kdb_machreg_t)(&kdb)); /* for traceback */ + break; + case KDB_REASON_PANIC: + kdb_printf("Panic: %s\n", kdb_diemsg); + kdb_printf("due to panic @ " kdb_machreg_fmt "\n", kdba_getpc(ef)); + kdba_dumpregs(ef, NULL, NULL); + break; + case KDB_REASON_NMI: + kdb_printf("due to NonMaskable Interrupt @ " kdb_machreg_fmt "\n", + kdba_getpc(ef)); + kdba_dumpregs(ef, NULL, NULL); + break; + case KDB_REASON_WATCHDOG: + kdb_printf("due to WatchDog Interrupt @ " kdb_machreg_fmt "\n", + kdba_getpc(ef)); + kdba_dumpregs(ef, NULL, NULL); + break; + case KDB_REASON_BREAK: + kdb_printf("due to Breakpoint @ " kdb_machreg_fmt "\n", kdba_getpc(ef)); + /* + * Determine if this breakpoint is one that we + * are interested in. + */ + if (db_result != KDB_DB_BPT) { + kdb_printf("kdb: error return from kdba_bp_trap: %d\n", db_result); + return 0; /* Not for us, dismiss it */ + } + break; + case KDB_REASON_RECURSE: + kdb_printf("due to Recursion @ " kdb_machreg_fmt "\n", kdba_getpc(ef)); + break; + case KDB_REASON_SILENT: + return KDB_CMD_GO; /* Silent entry, silent exit */ + break; + default: + kdb_printf("kdb: unexpected reason code: %d\n", reason); + return 0; /* Not for us, dismiss it */ + } + + while (1) { + /* + * Initialize pager context. + */ + kdb_nextline = 1; +#ifdef KDB_HAVE_LONGJMP + /* + * Use kdb_setjmp/kdb_longjmp to break out of + * the pager early. + */ + if (kdb_setjmp(&kdbjmpbuf[smp_processor_id()])) { + /* + * Command aborted (usually in pager) + */ + + /* + * XXX - need to abort a SSB ? + */ + continue; + } +#endif /* KDB_HAVE_LONGJMP */ + +do_full_getstr: +#if defined(CONFIG_SMP) + kdb_printf(kdbgetenv("PROMPT"), smp_processor_id()); +#else + kdb_printf(kdbgetenv("PROMPT")); +#endif + + + cmdbuf = cmd_hist[cmd_head]; + *cmdbuf = '\0'; +get_again: + /* + * Fetch command from keyboard + */ + cmdbuf = kdb_getstr(cmdbuf, CMD_BUFLEN,""); + if (*cmdbuf < 32) { + int not_cr = *cmdbuf == '\n' ? 0 : 1; + + if (!(not_cr || parsed_once) + || handle_ctrl_cmd(cmdbuf)) goto do_full_getstr; + + if (not_cr) goto get_again; + } + + if (*cmdbuf != '\n') { + cmd_head = (cmd_head+1) % KDB_CMD_HISTORY_COUNT; + if (cmd_head == cmd_tail) cmd_tail = (cmd_tail+1) % KDB_CMD_HISTORY_COUNT; + + parsed_once = 1; + } + + cmdptr = cmd_head; + strcpy(cmd, cmdbuf); /* copy because of destructive parsing */ + diag = kdb_parse(cmd, ef); + if (diag == KDB_NOTFOUND) { + kdb_printf("Unknown kdb command: '%s'\n", cmd); + diag = 0; + } + if (diag == KDB_CMD_GO + || diag == KDB_CMD_CPU + || diag == KDB_CMD_SS + || diag == KDB_CMD_SSB) + break; + + if (diag) + kdb_cmderror(diag); + } + + return(diag); +} + + +/* + * kdb_print_state + * + * Print the state data for the current processor for debugging. + * + * Inputs: + * text Identifies the debug point + * value Any integer value to be printed, e.g. reason code. + * Returns: + * None. + * Locking: + * none + * Remarks: + * none + */ + +void kdb_print_state(const char *text, int value) +{ + kdb_printf("state: %s cpu %d value %d initial %d state %x\n", + text, smp_processor_id(), value, kdb_initial_cpu, kdb_state[smp_processor_id()]); +} + +/* + * kdb_previous_event + * + * Return a count of cpus that are leaving kdb, i.e. the number + * of processors that are still handling the previous kdb event. + * + * Inputs: + * None. + * Returns: + * Count of cpus in previous event. + * Locking: + * none + * Remarks: + * none + */ + +static int +kdb_previous_event(void) +{ + int i, leaving = 0; + for (i = 0; i < NR_CPUS; ++i) { + if (KDB_STATE_CPU(LEAVING, i)) + ++leaving; + } + return(leaving); +} + +/* + * kdb_main_loop + * + * The main kdb loop. After initial setup and assignment of the controlling + * cpu, all cpus are in this loop. One cpu is in control and will issue the kdb + * prompt, the others will spin until 'go' or cpu switch. + * + * To get a consistent view of the kernel stacks for all processes, this routine + * is invoked from the main kdb code via an architecture specific routine. + * kdba_main_loop is responsible for making the kernel stacks consistent for all + * processes, there should be no difference between a blocked process and a + * running process as far as kdb is concerned. + * + * Inputs: + * reason The reason KDB was invoked + * error The hardware-defined error code + * reason2 kdb's current reason code. Initially error but can change + * acording to kdb state. + * db_result Result code from break or debug point. + * ef The exception frame at time of fault/breakpoint. If reason + * is KDB_REASON_SILENT then ef is NULL, otherwise it should + * always be valid. + * Returns: + * 0 KDB was invoked for an event which it wasn't responsible + * 1 KDB handled the event for which it was invoked. + * Locking: + * none + * Remarks: + * none + */ +int +kdb_main_loop(kdb_reason_t reason, kdb_reason_t reason2, int error, + kdb_dbtrap_t db_result, kdb_eframe_t ef) +{ + int result = 1; + /* Stay in kdb() until 'go', 'ss[b]' or an error */ + while (1) { + int i; + /* + * All processors except the one that is in control + * will spin here. + */ + KDB_DEBUG_STATE("kdb_main_loop 1", reason); + while (KDB_STATE(HOLD_CPU)) + ; + KDB_DEBUG_STATE("kdb_main_loop 2", reason); + if (KDB_STATE(LEAVING)) + break; /* Another cpu said 'go' */ + + /* Still using kdb, this processor is in control */ + result = kdb_local(reason2, error, ef, db_result); + KDB_DEBUG_STATE("kdb_main_loop 3", result); + + if (result == KDB_CMD_CPU) { + /* Cpu switch, hold the current cpu, release the target one. */ + reason2 = KDB_REASON_SWITCH; + KDB_STATE_SET(HOLD_CPU); + KDB_STATE_CLEAR_CPU(HOLD_CPU, kdb_new_cpu); + continue; + } + + if (result == KDB_CMD_SS) { + KDB_STATE_SET(DOING_SS); + break; + } + + if (result == KDB_CMD_SSB) { + KDB_STATE_SET(DOING_SS); + KDB_STATE_SET(DOING_SSB); + break; + } + + if (result && result != 1 && result != KDB_CMD_GO) + kdb_printf("\nUnexpected kdb_local return code %d\n", result); + + /* + * All other return codes (including KDB_CMD_GO) from + * kdb_local will end kdb(). Release all other cpus + * which will see KDB_STATE(LEAVING) is set. + */ + for (i = 0; i < NR_CPUS; ++i) { + if (KDB_STATE_CPU(KDB, i)) + KDB_STATE_SET_CPU(LEAVING, i); + KDB_STATE_CLEAR_CPU(WAIT_IPI, i); + KDB_STATE_CLEAR_CPU(HOLD_CPU, i); + } + KDB_DEBUG_STATE("kdb_main_loop 4", reason); + break; + } + return(result != 0); +} +/* + * kdb + * + * This function is the entry point for the kernel debugger. It + * provides a command parser and associated support functions to + * allow examination and control of an active kernel. + * + * This function may be invoked directly from any + * point in the kernel by calling with reason == KDB_REASON_CALL + * (XXX - note that the regs aren't set up this way - could + * use a software interrupt to enter kdb to get regs...) + * + * The breakpoint trap code should invoke this function with + * one of KDB_REASON_BREAK (int 03) or KDB_REASON_DEBUG (debug register) + * + * the panic function should invoke this function with + * KDB_REASON_PANIC. + * + * The kernel fault handler should invoke this function with + * reason == KDB_REASON_FAULT and error == trap vector #. + * + * In single step mode, one cpu is released to run without + * breakpoints. Interrupts and NMI are reset to their original values, + * the cpu is allowed to do one instruction which causes a trap + * into kdb with KDB_REASON_DEBUG. + * + * Inputs: + * reason The reason KDB was invoked + * error The hardware-defined error code + * ef The exception frame at time of fault/breakpoint. If reason + * is KDB_REASON_SILENT then ef is NULL, otherwise it should + * always be valid. + * Returns: + * 0 KDB was invoked for an event which it wasn't responsible + * 1 KDB handled the event for which it was invoked. + * Locking: + * none + * Remarks: + * No assumptions of system state. This function may be invoked + * with arbitrary locks held. It will stop all other processors + * in an SMP environment, disable all interrupts and does not use + * the operating systems keyboard driver. + * + * This code is reentrant but only for cpu switch. Any other + * reentrancy is an error, although kdb will attempt to recover. + * + * At the start of a kdb session the initial processor is running + * kdb() and the other processors can be doing anything. When the + * initial processor calls smp_kdb_stop() the other processors are + * driven through kdb_ipi which calls kdb() with reason SWITCH. + * That brings all processors into this routine, one with a "real" + * reason code, the other with SWITCH. + * + * Because the other processors are driven via smp_kdb_stop(), + * they enter here from the NMI handler. Until the other + * processors exit from here and exit from kdb_ipi, they will not + * take any more NMI requests. The initial cpu will still take NMI. + * + * Multiple race and reentrancy conditions, each with different + * advoidance mechanisms. + * + * Two cpus hit debug points at the same time. + * + * kdb_lock and kdb_initial_cpu ensure that only one cpu gets + * control of kdb. The others spin on kdb_initial_cpu until + * they are driven through NMI into kdb_ipi. When the initial + * cpu releases the others from NMI, they resume trying to get + * kdb_initial_cpu to start a new event. + * + * A cpu is released from kdb and starts a new event before the + * original event has completely ended. + * + * kdb_previous_event() prevents any cpu from entering + * kdb_initial_cpu state until the previous event has completely + * ended on all cpus. + * + * An exception occurs inside kdb. + * + * kdb_initial_cpu detects recursive entry to kdb and attempts + * to recover. The recovery uses longjmp() which means that + * recursive calls to kdb never return. Beware of assumptions + * like + * + * ++depth; + * kdb(); + * --depth; + * + * If the kdb call is recursive then longjmp takes over and + * --depth is never executed. + * + * NMI handling. + * + * NMI handling is tricky. The initial cpu is invoked by some kdb event, + * this event could be NMI driven but usually is not. The other cpus are + * driven into kdb() via kdb_ipi which uses NMI so at the start the other + * cpus will not accept NMI. Some operations such as SS release one cpu + * but hold all the others. Releasing a cpu means it drops back to + * whatever it was doing before the kdb event, this means it drops out of + * kdb_ipi and hence out of NMI status. But the software watchdog uses + * NMI and we do not want spurious watchdog calls into kdb. + * KDB_STATE(NO_WATCHDOG) prevents the software watchdog calling kdb. + * This flag is set while the cpu is inside mainline kdb, it is not set + * during a kdb command, in order to detect looping commands. + * + * Another problem with NMI handling is the NMI used to drive the other + * cpus into kdb cannot be distinguished from the watchdog NMI. State + * flag WAIT_IPI indicates that a cpu is waiting for NMI via kdb_ipi, + * if not set then software NMI is ignored by kdb_ipi. + * + * Cpu switching. + * + * All cpus are in kdb (or they should be), all but one are + * spinning on KDB_STATE(HOLD_CPU). Only one cpu is not in + * HOLD_CPU state, only that cpu can handle commands. + * + */ + +int +kdb(kdb_reason_t reason, int error, kdb_eframe_t ef) +{ + kdb_intstate_t int_state; /* Interrupt state */ + kdb_reason_t reason2 = reason; + int result = 1; /* Default is kdb handled it */ + int ss_event; + kdb_dbtrap_t db_result=KDB_DB_NOBPT; + + if (!kdb_on) + return 0; + + KDB_DEBUG_STATE("kdb 1", reason); + + /* Filter out userspace breakpoints first, no point in doing all + * the kdb smp fiddling when it is really a gdb trap. + * Save the single step status first, kdba_db_trap clears ss status. + */ + ss_event = (KDB_STATE(DOING_SS) || KDB_STATE(SSBPT)); + if (reason == KDB_REASON_BREAK) + db_result = kdba_bp_trap(ef); /* Only call this once */ + if (reason == KDB_REASON_DEBUG) + db_result = kdba_db_trap(ef); /* Only call this once */ + + if ((reason == KDB_REASON_BREAK || reason == KDB_REASON_DEBUG) + && db_result == KDB_DB_NOBPT) { + KDB_DEBUG_STATE("kdb 2", reason); + return 0; /* Not one of mine */ + } + + /* Turn off single step if it was being used */ + if (ss_event) + kdba_clearsinglestep(ef); + + /* kdb can validly reenter but only for certain well defined conditions */ + if (reason == KDB_REASON_DEBUG + && !KDB_STATE(HOLD_CPU) + && ss_event) + KDB_STATE_SET(REENTRY); + else + KDB_STATE_CLEAR(REENTRY); + + /* Wait for previous kdb event to completely exit before starting + * a new event. + */ + KDB_STATE_SET(NO_WATCHDOG); + while (kdb_previous_event()) + ; + KDB_DEBUG_STATE("kdb 3", reason); + + /* + * If kdb is already active, print a message and try to recover. + * If recovery is not possible and recursion is allowed or + * forced recursion without recovery is set then try to recurse + * in kdb. Not guaranteed to work but it makes an attempt at + * debugging the debugger. + */ + if (reason != KDB_REASON_SWITCH) { + if (KDB_IS_RUNNING() && !KDB_STATE(REENTRY)) { + int recover = 1; + unsigned long recurse = 0; + kdb_printf("kdb: Debugger re-entered on cpu %d, new reason = %d\n", + smp_processor_id(), reason); + /* Should only re-enter from released cpu */ + if (KDB_STATE(HOLD_CPU)) { + kdb_printf(" Strange, cpu %d should not be running\n", smp_processor_id()); + recover = 0; + } + if (!KDB_STATE(CMD)) { + kdb_printf(" Not executing a kdb command\n"); + recover = 0; + } + if (!KDB_STATE(LONGJMP)) { + kdb_printf(" No longjmp available for recovery\n"); + recover = 0; + } + kdbgetulenv("RECURSE", &recurse); + if (recurse > 1) { + kdb_printf(" Forced recursion is set\n"); + recover = 0; + } + if (recover) { + kdb_printf(" Attempting to abort command and recover\n"); +#ifdef KDB_HAVE_LONGJMP + kdb_longjmp(&kdbjmpbuf[smp_processor_id()], 0); +#endif + } + if (recurse) { + if (KDB_STATE(RECURSE)) { + kdb_printf(" Already in recursive mode\n"); + } else { + kdb_printf(" Attempting recursive mode\n"); + KDB_STATE_SET(RECURSE); + KDB_STATE_SET(REENTRY); + reason2 = KDB_REASON_RECURSE; + recover = 1; + } + } + if (!recover) { + kdb_printf(" Cannot recover, allowing event to proceed\n"); + KDB_STATE_CLEAR(NO_WATCHDOG); + return(0); + } + } + } else if (!KDB_IS_RUNNING()) { + kdb_printf("kdb: CPU switch without kdb running, I'm confused\n"); + KDB_STATE_CLEAR(NO_WATCHDOG); + return(0); + } + + /* + * Disable interrupts, breakpoints etc. on this processor + * during kdb command processing + */ + KDB_STATE_SET(KDB); + kdba_disableint(&int_state); + if (!KDB_STATE(KDB_CONTROL)) { + kdb_bp_remove_local(); + kdba_disable_lbr(); + KDB_STATE_SET(KDB_CONTROL); + } + else if (KDB_DEBUG(LBR)) + kdba_print_lbr(); + + /* + * If not entering the debugger due to CPU switch or single step + * reentry, serialize access here. + * The processors may race getting to this point - if, + * for example, more than one processor hits a breakpoint + * at the same time. We'll serialize access to kdb here - + * other processors will loop here, and the NMI from the stop + * IPI will take them into kdb as switch candidates. Once + * the initial processor releases the debugger, the rest of + * the processors will race for it. + */ + if (reason == KDB_REASON_SWITCH + || KDB_STATE(REENTRY)) + ; /* drop through */ + else { + KDB_DEBUG_STATE("kdb 4", reason); + spin_lock(&kdb_lock); + + while (KDB_IS_RUNNING() || kdb_previous_event()) { + spin_unlock(&kdb_lock); + + while (KDB_IS_RUNNING() || kdb_previous_event()) + ; + + spin_lock(&kdb_lock); + } + KDB_DEBUG_STATE("kdb 5", reason); + + kdb_initial_cpu = smp_processor_id(); + spin_unlock(&kdb_lock); + } + + if (smp_processor_id() == kdb_initial_cpu + && !KDB_STATE(REENTRY)) { + KDB_STATE_CLEAR(HOLD_CPU); + KDB_STATE_CLEAR(WAIT_IPI); + /* + * Remove the global breakpoints. This is only done + * once from the initial processor on initial entry. + */ + kdb_bp_remove_global(); + + /* + * If SMP, stop other processors. The other processors + * will enter kdb() with KDB_REASON_SWITCH and spin + * below. + */ + KDB_DEBUG_STATE("kdb 6", reason); + if (smp_num_cpus > 1) { + int i; + for (i = 0; i < NR_CPUS; ++i) { + if (i != kdb_initial_cpu) { + KDB_STATE_SET_CPU(HOLD_CPU, i); + KDB_STATE_SET_CPU(WAIT_IPI, i); + } + } + smp_kdb_stop(); + } + } + + /* Set up a consistent set of process stacks before talking to the user */ + result = kdba_main_loop(reason, reason2, error, db_result, ef); + + KDB_DEBUG_STATE("kdb 7", result); + kdba_adjust_ip(reason, error, ef); + KDB_STATE_CLEAR(LONGJMP); + + /* No breakpoints installed for SS */ + if (!KDB_STATE(DOING_SS) && + !KDB_STATE(SSBPT) && + !KDB_STATE(RECURSE)) { + kdba_enable_lbr(); + if (ef) + kdb_bp_install_local(ef); + KDB_STATE_CLEAR(KDB_CONTROL); + } + + kdba_restoreint(&int_state); + + KDB_STATE_CLEAR(KDB); /* Main kdb state has been cleared */ + KDB_STATE_CLEAR(LEAVING); /* Elvis has left the building ... */ + + if (smp_processor_id() == kdb_initial_cpu && + !KDB_STATE(DOING_SS) && + !KDB_STATE(RECURSE)) { + /* + * (Re)install the global breakpoints. This is only done + * once from the initial processor on final exit. + */ + KDB_DEBUG_STATE("kdb 8", reason); + if (ef) + kdb_bp_install_global(ef); + /* Wait until all the other processors leave kdb */ + while (kdb_previous_event()) + ; + kdb_initial_cpu = -1; /* release kdb control */ + KDB_DEBUG_STATE("kdb 9", reason); + } + + KDB_STATE_CLEAR(NO_WATCHDOG); + KDB_STATE_CLEAR(RECURSE); + KDB_DEBUG_STATE("kdb 10", reason); + return(result != 0); +} + +/* + * kdb_mdr + * + * This function implements the guts of the 'mdr' command. + * + * mdr , + * + * Inputs: + * addr Start address + * count Number of bytes + * Outputs: + * None. + * Returns: + * Always 0. Any errors are detected and printed by kdba_getword. + * Locking: + * none. + * Remarks: + */ + +static int +kdb_mdr(kdb_machreg_t addr, unsigned int count) +{ + kdb_machreg_t addr2 = addr; + unsigned int count2 = count; + + KDB_STATE_CLEAR(SUPPRESS); + while (count2--) { + kdba_getword(addr2++, 1); + if (KDB_STATE(SUPPRESS)) { + KDB_STATE_CLEAR(SUPPRESS); + return(0); /* Error message already printed */ + } + } + + while (count--) + kdb_printf("%02lx", kdba_getword(addr++, 1)); + kdb_printf("\n"); + return(0); +} + +/* + * kdb_md + * + * This function implements the 'md', 'mdr' and 'mds' commands. + * + * md|mds [ [ []]] + * mdr , + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_md(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + char fmtchar; + char fmtstr[64]; + int radix, count, width; + kdb_machreg_t addr; + unsigned long word; + long offset = 0; + kdb_symtab_t symtab; + int diag; + int nextarg; + int nosect = 0; + static kdb_machreg_t lastaddr; + static unsigned long lastcount; + static unsigned long lastradix; + char lastbuf[50]; + int symbolic = 0; + + /* + * Defaults in case the relevent environment variables are unset + */ + radix = 16; + count = 8; + width = sizeof(kdb_machreg_t); + + if (strcmp(argv[0], "mdr") == 0 && argc != 2) + return KDB_ARGCOUNT; + + if (argc == 0) { + if (lastaddr == 0) + return KDB_ARGCOUNT; + sprintf(lastbuf, kdb_machreg_fmt, lastaddr); + argv[1] = lastbuf; + argc = 1; + count = lastcount; + radix = lastradix; + } else { + kdb_machreg_t val; + + if (argc >= 2) { + + diag = kdbgetularg(argv[2], &val); + if (!diag) + count = (int) val; + } else { + diag = kdbgetintenv("MDCOUNT", &count); + } + + if (argc >= 3) { + diag = kdbgetularg(argv[3], &val); + if (!diag) + radix = (int) val; + } else { + diag = kdbgetintenv("RADIX",&radix); + } + } + + switch (radix) { + case 10: + fmtchar = 'd'; + break; + case 16: + fmtchar = 'x'; + break; + case 8: + fmtchar = 'o'; + break; + default: + return KDB_BADRADIX; + } + + kdbgetintenv("BYTESPERWORD", &width); + kdbgetintenv("NOSECT", &nosect); + + if (strcmp(argv[0], "mds") == 0) { + symbolic = 1; + width = sizeof(kdb_machreg_t); + } + + switch (width) { + case 8: + sprintf(fmtstr, "%%16.16l%c ", fmtchar); + break; + case 4: + sprintf(fmtstr, "%%8.8l%c ", fmtchar); + break; + case 2: + sprintf(fmtstr, "%%4.4l%c ", fmtchar); + break; + case 1: + sprintf(fmtstr, "%%2.2l%c ", fmtchar); + break; + default: + return KDB_BADWIDTH; + } + + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + if (strcmp(argv[0], "mdr") == 0) { + return(kdb_mdr(addr, count)); + } + + /* Round address down modulo BYTESPERWORD */ + + addr &= ~(width-1); + + /* + * Remember count and radix for next 'md' + */ + lastcount = count; + lastradix = radix; + + while (count--) { + int num = (symbolic?1 :(16 / width)); + char cbuf[32]; + char *c = cbuf; + char t; + int i; + + for(i=0; i argc) + return KDB_ARGCOUNT; + + diag = kdbgetaddrarg(argc, argv, &nextarg, &contents, NULL, NULL, regs); + if (diag) + return diag; + + if (nextarg != argc + 1) + return KDB_ARGCOUNT; + + /* + * To prevent modification of invalid addresses, check first. + */ + word = kdba_getword(addr, sizeof(word)); + if (KDB_STATE(SUPPRESS)) { + KDB_STATE_CLEAR(SUPPRESS); + return 0; + } + + diag = kdba_putword(addr, sizeof(contents), contents); + + kdb_printf(kdb_machreg_fmt " = " kdb_machreg_fmt "\n", addr, contents); + + return 0; +} + +/* + * kdb_go + * + * This function implements the 'go' command. + * + * go [address-expression] + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * KDB_CMD_GO for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_go(int argc, const char **argv, const char **envp, kdb_eframe_t ef) +{ + kdb_machreg_t addr; + int diag; + int nextarg; + long offset; + + if (argc == 1) { + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, + &addr, &offset, NULL, ef); + if (diag) + return diag; + + kdba_setpc(ef, addr); + } else if (argc) + return KDB_ARGCOUNT; + + return KDB_CMD_GO; +} + +/* + * kdb_rd + * + * This function implements the 'rd' command. + * + * rd display all general registers. + * rd c display all control registers. + * rd d display all debug registers. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_rd(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + /* + */ + + if (argc == 0) { + return kdba_dumpregs(regs, NULL, NULL); + } + + if (argc > 2) { + return KDB_ARGCOUNT; + } + + return kdba_dumpregs(regs, argv[1], argv[2]); +} + +/* + * kdb_rm + * + * This function implements the 'rm' (register modify) command. + * + * rm register-name new-contents + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Currently doesn't allow modification of control or + * debug registers, nor does it allow modification + * of model-specific registers (MSR). + */ + +int +kdb_rm(int argc, const char **argv, const char **envp, kdb_eframe_t ef) +{ + int diag; + int ind = 0; + kdb_machreg_t contents; + + if (argc != 2) { + return KDB_ARGCOUNT; + } + + /* + * Allow presence or absence of leading '%' symbol. + */ + + if (argv[1][0] == '%') + ind = 1; + + diag = kdbgetularg(argv[2], &contents); + if (diag) + return diag; + + diag = kdba_setregcontents(&argv[1][ind], ef, contents); + if (diag) + return diag; + + return 0; +} + +#if defined(CONFIG_MAGIC_SYSRQ) +/* + * kdb_sr + * + * This function implements the 'sr' (SYSRQ key) command which + * interfaces to the soi-disant MAGIC SYSRQ functionality. + * + * sr + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * None. + */ +int +kdb_sr(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + if (argc != 1) { + return KDB_ARGCOUNT; + } + + handle_sysrq(*argv[1], regs, 0, 0); + + return 0; +} +#endif /* CONFIG_MAGIC_SYSRQ */ + +/* + * kdb_ef + * + * This function implements the 'ef' (display exception frame) + * command. This command takes an address and expects to find + * an exception frame at that address, formats and prints it. + * + * ef address-expression + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Not done yet. + */ + +int +kdb_ef(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int diag; + kdb_machreg_t addr; + long offset; + int nextarg; + + if (argc == 1) { + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + return kdba_dumpregs((struct pt_regs *)addr, NULL, NULL); + } + + return KDB_ARGCOUNT; +} + +/* + * kdb_reboot + * + * This function implements the 'reboot' command. Reboot the system + * immediately. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Shouldn't return from this function. + */ + +int +kdb_reboot(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + machine_restart(0); + /* NOTREACHED */ + return 0; +} + + +#if defined(CONFIG_MODULES) +extern struct module *find_module(const char *); +extern void free_module(struct module *, int); + +/* + * kdb_lsmod + * + * This function implements the 'lsmod' command. Lists currently + * loaded kernel modules. + * + * Mostly taken from userland lsmod. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * + */ + +int +kdb_lsmod(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + struct module *mod; + struct module_ref *mr; + + if (argc != 0) + return KDB_ARGCOUNT; + + kdb_printf("Module Size modstruct Used by\n"); + for (mod = module_list; mod && mod->next ;mod = mod->next) { + kdb_printf("%-20s%8lu 0x%p %4ld ", mod->name, mod->size, (void *)mod, + (long)atomic_read(&mod->uc.usecount)); + + if (mod->flags & MOD_DELETED) + kdb_printf(" (deleted)"); + else if (mod->flags & MOD_INITIALIZING) + kdb_printf(" (initializing)"); + else if (!(mod->flags & MOD_RUNNING)) + kdb_printf(" (uninitialized)"); + else { + if (mod->flags & MOD_AUTOCLEAN) + kdb_printf(" (autoclean)"); + if (!(mod->flags & MOD_USED_ONCE)) + kdb_printf(" (unused)"); + } + + if (mod->refs) { + kdb_printf(" [ "); + + mr = mod->refs; + while (mr) { + kdb_printf("%s ", mr->ref->name); + mr = mr->next_ref; + } + + kdb_printf("]"); + } + + kdb_printf("\n"); + } + + return 0; +} + +/* + * kdb_rmmod + * + * This function implements the 'rmmod' command. Removes a given + * kernel module. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Danger: free_module() calls mod->cleanup(). If the cleanup routine + * relies on interrupts then it will hang, kdb has interrupts disabled. + */ + +int +kdb_rmmod(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + struct module *mod; + + + if (argc != 1) + return KDB_ARGCOUNT; + + kdb_printf("Attempting to remove module: [%s]\n", argv[1]); + if ((mod = find_module(argv[1])) == NULL) { + kdb_printf("Unable to find a module by that name\n"); + return 0; + } + + if (mod->refs != NULL || __MOD_IN_USE(mod)) { + kdb_printf("Module is in use, unable to unload\n"); + return 0; + } + + free_module(mod, 0); + kdb_printf("Module successfully unloaded\n"); + + return 0; +} +#endif /* CONFIG_MODULES */ + +/* + * kdb_env + * + * This function implements the 'env' command. Display the current + * environment variables. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_env(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + + for(i=0; i<__nenv; i++) { + if (__env[i]) { + kdb_printf("%s\n", __env[i]); + } + } + + if (KDB_DEBUG(MASK)) + kdb_printf("KDBFLAGS=0x%x\n", kdb_flags); + + return 0; +} + +/* + * kdb_set + * + * This function implements the 'set' command. Alter an existing + * environment variable or create a new one. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_set(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + char *ep; + size_t varlen, vallen; + + /* + * we can be invoked two ways: + * set var=value argv[1]="var", argv[2]="value" + * set var = value argv[1]="var", argv[2]="=", argv[3]="value" + * - if the latter, shift 'em down. + */ + if (argc == 3) { + argv[2] = argv[3]; + argc--; + } + + if (argc != 2) + return KDB_ARGCOUNT; + + /* + * Check for internal variables + */ + if (strcmp(argv[1], "KDBDEBUG") == 0) { + unsigned int debugflags; + char *cp; + + debugflags = simple_strtoul(argv[2], &cp, 0); + if (cp == argv[2] || debugflags & ~KDB_DEBUG_FLAG_MASK) { + kdb_printf("kdb: illegal debug flags '%s'\n", + argv[2]); + return 0; + } + kdb_flags = (kdb_flags & ~(KDB_DEBUG_FLAG_MASK << KDB_DEBUG_FLAG_SHIFT)) + | (debugflags << KDB_DEBUG_FLAG_SHIFT); + + return 0; + } + + /* + * Tokenizer squashed the '=' sign. argv[1] is variable + * name, argv[2] = value. + */ + varlen = strlen(argv[1]); + vallen = strlen(argv[2]); + ep = kdballocenv(varlen + vallen + 2); + if (ep == (char *)0) + return KDB_ENVBUFFULL; + + sprintf(ep, "%s=%s", argv[1], argv[2]); + + ep[varlen+vallen+1]='\0'; + + for(i=0; i<__nenv; i++) { + if (__env[i] + && ((strncmp(__env[i], argv[1], varlen)==0) + && ((__env[i][varlen] == '\0') + || (__env[i][varlen] == '=')))) { + __env[i] = ep; + return 0; + } + } + + /* + * Wasn't existing variable. Fit into slot. + */ + for(i=0; i<__nenv-1; i++) { + if (__env[i] == (char *)0) { + __env[i] = ep; + return 0; + } + } + + return KDB_ENVFULL; +} + +/* + * kdb_cpu + * + * This function implements the 'cpu' command. + * + * cpu [] + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * KDB_CMD_CPU for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * All cpu's should be spinning in kdb(). However just in case + * a cpu did not take the smp_kdb_stop NMI, check that a cpu + * entered kdb() before passing control to it. + */ + +int +kdb_cpu(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + unsigned long cpunum; + int diag; + + if (argc == 0) { + int i; + + kdb_printf("Currently on cpu %d\n", smp_processor_id()); + kdb_printf("Available cpus: "); + for (i=0; i NR_CPUS) + || !(cpu_online_map & (1UL << cpunum)) + || !KDB_STATE_CPU(KDB, cpunum)) + return KDB_BADCPUNUM; + + kdb_new_cpu = cpunum; + + /* + * Switch to other cpu + */ + return KDB_CMD_CPU; +} + +/* + * kdb_ps + * + * This function implements the 'ps' command which shows + * a list of the active processes. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_ps(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + struct task_struct *p; + + kdb_printf("Task Addr Pid Parent [*] cpu State Thread Command\n"); + for_each_task(p) { + kdb_printf("0x%p %08d %08d %1.1d %3.3d %s 0x%p%c%s\n", + (void *)p, p->pid, p->p_pptr->pid, + p->has_cpu, p->processor, + (p->state == 0)?"run ":(p->state>0)?"stop":"unrn", + (void *)(&p->thread), + (p == current) ? '*': ' ', + p->comm); + } + + return 0; +} + +/* + * kdb_ll + * + * This function implements the 'll' command which follows a linked + * list and executes an arbitrary command for each element. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_ll(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int diag; + kdb_machreg_t addr; + long offset = 0; + kdb_machreg_t va; + unsigned long linkoffset; + int nextarg; + + if (argc != 3) { + return KDB_ARGCOUNT; + } + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + diag = kdbgetularg(argv[2], &linkoffset); + if (diag) + return diag; + + /* + * Using the starting address as + * the first element in the list, and assuming that + * the list ends with a null pointer. + */ + + va = addr; + + while (va) { + char buf[80]; + + sprintf(buf, "%s " kdb_machreg_fmt "\n", argv[3], va); + diag = kdb_parse(buf, regs); + if (diag) + return diag; + + addr = va + linkoffset; + va = kdba_getword(addr, sizeof(va)); + if (KDB_STATE(SUPPRESS)) { + KDB_STATE_CLEAR(SUPPRESS); + return 0; + } + } + + return 0; +} + +/* + * kdb_sections_callback + * + * Invoked from kallsyms_sections for each section. + * + * Inputs: + * prevmod Previous module name + * modname Module name + * secname Section name + * secstart Start of section + * secend End of section + * secflags Section flags + * Outputs: + * None. + * Returns: + * Always zero + * Locking: + * none. + * Remarks: + */ + +static int +kdb_sections_callback(void *token, const char *modname, const char *secname, + ElfW(Addr) secstart, ElfW(Addr) secend, ElfW(Word) secflags) +{ + const char **prevmod = (const char **)token; + if (*prevmod != modname) { + *prevmod = modname; + kdb_printf("\n%s", modname); + } + kdb_printf(" %s " kdb_elfw_addr_fmt0 " " kdb_elfw_addr_fmt0 " 0x%x", + secname, secstart, secend, secflags); + return(0); +} + +/* + * kdb_sections + * + * This function implements the 'sections' command which prints the + * kernel and module sections. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * Always zero + * Locking: + * none. + * Remarks: + */ + +int +kdb_sections(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + char *prev_mod = NULL; + if (argc != 0) { + return KDB_ARGCOUNT; + } + kallsyms_sections(&prev_mod, kdb_sections_callback); + kdb_printf("\n"); /* End last module */ + return(0); +} + +/* + * kdb_help + * + * This function implements the 'help' and '?' commands. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_help(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + kdbtab_t *kt; + + kdb_printf("%-15.15s %-20.20s %s\n", "Command", "Usage", "Description"); + kdb_printf("----------------------------------------------------------\n"); + for(kt=kdb_commands; kt->cmd_name; kt++) { + kdb_printf("%-15.15s %-20.20s %s\n", kt->cmd_name, + kt->cmd_usage, kt->cmd_help); + } + return 0; +} + +/* + * kdb_register + * + * This function is used to register a kernel debugger command. + * + * Inputs: + * cmd Command name + * func Function to execute the command + * usage A simple usage string showing arguments + * help A simple help string describing command + * Outputs: + * None. + * Returns: + * zero for success, one if a duplicate command. + * Locking: + * none. + * Remarks: + * + */ + +int +kdb_register(char *cmd, + kdb_func_t func, + char *usage, + char *help, + short minlen) +{ + int i; + kdbtab_t *kp; + + /* + * Brute force method to determine duplicates + */ + for (i=0, kp=kdb_commands; icmd_name && (strcmp(kp->cmd_name, cmd)==0)) { + kdb_printf("Duplicate kdb command registered: '%s'\n", + cmd); + return 1; + } + } + + /* + * Insert command into first available location in table + */ + for (i=0, kp=kdb_commands; icmd_name == NULL) { + kp->cmd_name = cmd; + kp->cmd_func = func; + kp->cmd_usage = usage; + kp->cmd_help = help; + kp->cmd_flags = 0; + kp->cmd_minlen = minlen; + break; + } + } + return 0; +} + +/* + * kdb_unregister + * + * This function is used to unregister a kernel debugger command. + * It is generally called when a module which implements kdb + * commands is unloaded. + * + * Inputs: + * cmd Command name + * Outputs: + * None. + * Returns: + * zero for success, one command not registered. + * Locking: + * none. + * Remarks: + * + */ + +int +kdb_unregister(char *cmd) +{ + int i; + kdbtab_t *kp; + + /* + * find the command. + */ + for (i=0, kp=kdb_commands; icmd_name && (strcmp(kp->cmd_name, cmd)==0)) { + kp->cmd_name = NULL; + return 0; + } + } + + /* + * Couldn't find it. + */ + return 1; +} + +/* + * kdb_inittab + * + * This function is called by the kdb_init function to initialize + * the kdb command table. It must be called prior to any other + * call to kdb_register. + * + * Inputs: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + */ + +static void __init +kdb_inittab(void) +{ + int i; + kdbtab_t *kp; + + for(i=0, kp=kdb_commands; i < KDB_MAX_COMMANDS; i++,kp++) { + kp->cmd_name = NULL; + } + + kdb_register("md", kdb_md, "", "Display Memory Contents", 1); + kdb_register("mdr", kdb_md, " ", "Display Raw Memory", 0); + kdb_register("mds", kdb_md, "", "Display Memory Symbolically", 0); + kdb_register("mm", kdb_mm, " ", "Modify Memory Contents", 0); + kdb_register("id", kdb_id, "", "Display Instructions", 1); + kdb_register("go", kdb_go, "[]", "Continue Execution", 1); + kdb_register("rd", kdb_rd, "", "Display Registers", 1); + kdb_register("rm", kdb_rm, " ", "Modify Registers", 0); + kdb_register("ef", kdb_ef, "", "Display exception frame", 0); + kdb_register("bt", kdb_bt, "[]", "Stack traceback", 1); + kdb_register("btp", kdb_bt, "", "Display stack for process ", 0); + kdb_register("bta", kdb_bt, "", "Display stack all processes", 0); + kdb_register("ll", kdb_ll, " ", "Execute cmd for each element in linked list", 0); + kdb_register("env", kdb_env, "", "Show environment variables", 0); + kdb_register("set", kdb_set, "", "Set environment variables", 0); + kdb_register("help", kdb_help, "", "Display Help Message", 1); + kdb_register("?", kdb_help, "", "Display Help Message", 0); + kdb_register("cpu", kdb_cpu, "","Switch to new cpu", 0); + kdb_register("ps", kdb_ps, "", "Display active task list", 0); + kdb_register("reboot", kdb_reboot, "", "Reboot the machine immediately", 0); + kdb_register("sections", kdb_sections, "", "List kernel and module sections", 0); +#if defined(CONFIG_MODULES) + kdb_register("lsmod", kdb_lsmod, "", "List loaded kernel modules", 0); + kdb_register("rmmod", kdb_rmmod, "", "Remove a kernel module", 1); +#endif +#if defined(CONFIG_MAGIC_SYSRQ) + kdb_register("sr", kdb_sr, "", "Magic SysRq key", 0); +#endif +} + +/* + * kdb_cmd_init + * + * This function is called by the kdb_init function to execute any + * commands defined in kdb_cmds. + * + * Inputs: + * Commands in *kdb_cmds[]; + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + */ + +static void __init +kdb_cmd_init(void) +{ + int i, diag; + for (i = 0; kdb_cmds[i]; ++i) { + kdb_printf("kdb_cmd[%d]: %s", i, kdb_cmds[i]); + diag = kdb_parse(kdb_cmds[i], NULL); + if (diag) + kdb_printf("command failed, kdb diag %d\n", diag); + } +} + +/* + * kdb_init + * + * Initialize the kernel debugger environment. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +void __init +kdb_init(void) +{ + /* + * This must be called before any calls to kdb_printf. + */ + kdb_io_init(); + + kdb_inittab(); /* Initialize Command Table */ + kdb_initbptab(); /* Initialize Breakpoint Table */ + kdb_id_init(); /* Initialize Disassembler */ + kdba_init(); /* Architecture Dependent Initialization */ + + /* + * Use printk() to get message in log_buf[]; + */ + printk("kdb version %d.%d%s by Scott Lurndal, Keith Owens. "\ + "Copyright SGI, All Rights Reserved\n", + KDB_MAJOR_VERSION, KDB_MINOR_VERSION, KDB_TEST_VERSION); + + kdb_cmd_init(); /* Preset commands from kdb_cmds */ + kdb(KDB_REASON_SILENT, 0, 0); /* Activate any preset breakpoints on boot cpu */ +} + +EXPORT_SYMBOL(kdb_register); +EXPORT_SYMBOL(kdb_unregister); +EXPORT_SYMBOL(kdba_getword); +EXPORT_SYMBOL(kdba_putword); +EXPORT_SYMBOL(kdbgetularg); +EXPORT_SYMBOL(kdbgetenv); +EXPORT_SYMBOL(kdbgetintenv); +EXPORT_SYMBOL(kdbgetaddrarg); +EXPORT_SYMBOL(kdb); +EXPORT_SYMBOL(kdbgetsymval); +EXPORT_SYMBOL(kdbnearsym); +EXPORT_SYMBOL(kdb_printf); +EXPORT_SYMBOL(kdb_symbol_print); diff -urN 2.4.0/kdb/kdbsupport.c 2.4.0-ikd-1/kdb/kdbsupport.c --- 2.4.0/kdb/kdbsupport.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kdb/kdbsupport.c Mon Jan 15 18:40:05 2001 @@ -0,0 +1,442 @@ +/* + * Kernel Debugger Architecture Independent Support Functions + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Symbol table functions. + */ + +/* + * kdbgetsymval + * + * Return the address of the given symbol. + * + * Parameters: + * symname Character string containing symbol name + * symtab Structure to receive results + * Outputs: + * Returns: + * 0 Symbol not found, symtab zero filled + * 1 Symbol mapped to module/symbol/section, data in symtab + * Locking: + * None. + * Remarks: + */ + +int +kdbgetsymval(const char *symname, kdb_symtab_t *symtab) +{ + memset(symtab, 0, sizeof(*symtab)); + return(kallsyms_symbol_to_address( + symname, + NULL, + &symtab->mod_name, + &symtab->mod_start, + &symtab->mod_end, + &symtab->sec_name, + &symtab->sec_start, + &symtab->sec_end, + &symtab->sym_name, + &symtab->sym_start, + &symtab->sym_end)); +} + +/* + * kdbnearsym + * + * Return the name of the symbol with the nearest address + * less than 'addr'. + * + * Parameters: + * addr Address to check for symbol near + * symtab Structure to receive results + * Outputs: + * Returns: + * 0 No sections contain this address, symtab zero filled + * 1 Address mapped to module/symbol/section, data in symtab + * Locking: + * None. + * Remarks: + */ + +int +kdbnearsym(unsigned long addr, kdb_symtab_t *symtab) +{ + memset(symtab, 0, sizeof(*symtab)); + return(kallsyms_address_to_symbol( + addr, + &symtab->mod_name, + &symtab->mod_start, + &symtab->mod_end, + &symtab->sec_name, + &symtab->sec_start, + &symtab->sec_end, + &symtab->sym_name, + &symtab->sym_start, + &symtab->sym_end)); +} + +#if defined(CONFIG_SMP) +/* + * kdb_ipi + * + * This function is called from the non-maskable interrupt + * handler to handle a kdb IPI instruction. + * + * Inputs: + * ef = Exception frame pointer + * Outputs: + * None. + * Returns: + * 0 - Did not handle NMI + * 1 - Handled NMI + * Locking: + * None. + * Remarks: + * Initially one processor is invoked in the kdb() code. That + * processor sends an ipi which drives this routine on the other + * processors. All this does is call kdb() with reason SWITCH. + * This puts all processors into the kdb() routine and all the + * code for breakpoints etc. is in one place. + * One problem with the way the kdb NMI is sent, the NMI has no + * identification that says it came from kdb. If the cpu's kdb state is + * marked as "waiting for kdb_ipi" then the NMI is treated as coming from + * kdb, otherwise it is assumed to be for another reason and is ignored. + */ + +int +kdb_ipi(kdb_eframe_t ef, void (*ack_interrupt)(void)) +{ + if (KDB_STATE(WAIT_IPI)) { + /* + * Stopping other processors via smp_kdb_stop(). + */ + if (ack_interrupt) + (*ack_interrupt)(); /* Acknowledge the interrupt */ + KDB_DEBUG_STATE("kdb_ipi 1", 0); + KDB_STATE_CLEAR(WAIT_IPI); + kdb(KDB_REASON_SWITCH, 0, ef); /* Spin in kdb() */ + KDB_DEBUG_STATE("kdb_ipi 2", 0); + return 1; + } + return 0; +} +#endif /* CONFIG_SMP */ + +void +kdb_enablehwfault(void) +{ + kdba_enable_mce(); +} + +/* + * kdb_get_next_ar + * + * Get the next activation record from the stack. + * + * Inputs: + * arend Last byte +1 of the activation record. sp for the first + * frame, start of callee's activation record otherwise. + * func Start address of function. + * pc Current program counter within this function. pc for + * the first frame, caller's return address otherwise. + * fp Current frame pointer. Register fp for the first + * frame, oldfp otherwise. 0 if not known. + * ss Start of stack for the current process. + * Outputs: + * ar Activation record. + * symtab kallsyms symbol table data for the calling function. + * Returns: + * 1 if ar is usable, 0 if not. + * Locking: + * None. + * Remarks: + * Activation Record format, assuming a stack that grows down + * (KDB_STACK_DIRECTION == -1). + * + * +-----------------------------+ ^ ===================== + * | Return address, frame 3 | | + * +-----------------------------+ | + * | Frame Pointer, frame 3 |>--' + * +-----------------------------+<--. + * | Locals and automatics, | | + * | frame 2. (variable size) | | AR 2 + * +-----------------------------+ | + * | Save registers, | | + * | frame 2. (variable size) | | + * +-----------------------------+ | + * | Arguments to frame 1, | | + * | (variable size) | | + * +-----------------------------+ | ===================== + * | Return address, frame 2 | | + * +-----------------------------+ | + * | Frame Pointer, frame 2 |>--' + * +-----------------------------+<--. + * | Locals and automatics, | | + * | frame 1. (variable size) | | AR 1 + * +-----------------------------+ | + * | Save registers, | | + * | frame 1. (variable size) | | + * +-----------------------------+ | + * | Arguments to frame 0, | | + * | (variable size) | | + * +-----------------------------+ | -- (5) ===================== + * | Return address, frame 1 | | + * +-----------------------------+ | -- (0) + * | Frame Pointer, frame 1 |>--' + * +-----------------------------+ -- (1), (2) + * | Locals and automatics, | + * | frame 0. (variable size) | AR 0 + * +-----------------------------+ -- (3) + * | Save registers, | + * | frame 0. (variable size) | + * +-----------------------------+ -- (4) ===================== + * + * The stack for the top frame can be in one of several states. + * (0) Immediately on entry to the function, stack pointer (sp) is + * here. + * (1) If the function was compiled with frame pointers and the 'push + * fp' instruction has been executed then the pointer to the + * previous frame is on the stack. However there is no guarantee + * that this saved pointer is valid, the calling function might + * not have frame pointers. sp is adjusted by wordsize after + * 'push fp'. + * (2) If the function was compiled with frame pointers and the 'copy + * sp to fp' instruction has been executed then fp points here. + * (3) If the function startup has 'adjust sp by 0xnn bytes' and that + * instruction has been executed then sp has been adjusted by + * 0xnn bytes for local and automatic variables. + * (4) If the function startup has one or more 'push reg' instructions + * and any have been executed then sp has been adjusted by + * wordsize bytes for each register saved. + * + * As the function exits it rewinds the stack, typically to (1) then (0). + * + * The stack entries for the lower frames is normally are in state (5). + * (5) Arguments for the called frame are on to the stack. + * However lower frames can be incomplete if there is an interrupt in + * progress. + * + * An activation record runs from the return address for a function + * through to the return address for the next function or sp, whichever + * comes first. For each activation record we extract :- + * + * start Address of the activation record. + * end Address of the last byte+1 in the activation record. + * ret Return address to caller. + * oldfp Frame pointer to previous frame, 0 if this function was + * not compiled with frame pointers. + * fp Frame pointer for the current frame, 0 if this function + * was not compiled with frame pointers or fp has not been + * set yet. + * arg0 Address of the first argument (in the previous activation + * record). + * locals Bytes allocated to locals and automatics. + * regs Bytes allocated to saved registers. + * args Bytes allocated to arguments (in the previous activation + * record). + * setup Bytes allocated to setup data on stack (return address, + * frame pointer). + * + * Although the kernel might be compiled with frame pointers, we still + * have to assume the worst and validate the frame. Some calls from + * asm code to C code might not use frame pointers. Third party binary + * only modules might be compiled without frame pointers, even when the + * rest of the kernel has frame pointers. Some routines are always + * compiled with frame pointers, even if the overall kernel is not. A + * routine compiled with frame pointers can be called from a routine + * without frame pointers, the previous "frame pointer" is saved on + * stack but it contains garbage. + * + * We check the object code to see if it saved a frame pointer and we + * validate that pointer. Basically frame pointers are hints. + */ + +#define FORCE_ARG(ar,n) (ar)->setup = (ar)->locals = (ar)->regs = \ + (ar)->fp = (ar)->oldfp = (ar)->ret = 0; \ + (ar)->start = (ar)->end - KDB_STACK_DIRECTION*(n)*sizeof(unsigned long); + +int +kdb_get_next_ar(kdb_machreg_t arend, kdb_machreg_t func, + kdb_machreg_t pc, kdb_machreg_t fp, kdb_machreg_t ss, + kdb_ar_t *ar, kdb_symtab_t *symtab) +{ + if (KDB_DEBUG(AR)) { + kdb_printf("kdb_get_next_ar: arend=0x%lx func=0x%lx pc=0x%lx fp=0x%lx\n", + arend, func, pc, fp); + } + + memset(ar, 0, sizeof(*ar)); + if (!kdbnearsym(pc, symtab)) { + symtab->sym_name = symtab->sec_name = ""; + symtab->mod_name = "kernel"; + if (KDB_DEBUG(AR)) { + kdb_printf("kdb_get_next_ar: callee not in kernel\n"); + } + pc = 0; + } + + if (!kdba_prologue(symtab, pc, arend, fp, ss, 0, ar)) { + if (KDB_DEBUG(AR)) { + kdb_printf("kdb_get_next_ar: callee prologue failed\n"); + } + return(0); + } + if (KDB_DEBUG(AR)) { + kdb_printf("kdb_get_next_ar: callee activation record\n"); + kdb_printf(" start=0x%lx end=0x%lx ret=0x%lx oldfp=0x%lx fp=0x%lx\n", + ar->start, ar->end, ar->ret, ar->oldfp, ar->fp); + kdb_printf(" locals=%d regs=%d setup=%d\n", + ar->locals, ar->regs, ar->setup); + } + + if (ar->ret) { + /* Run the caller code to get arguments to callee function */ + kdb_symtab_t caller_symtab; + kdb_ar_t caller_ar; + memset(&caller_ar, 0, sizeof(caller_ar)); + if (!kdbnearsym(ar->ret, &caller_symtab)) { + if (KDB_DEBUG(AR)) { + kdb_printf("kdb_get_next_ar: caller not in kernel\n"); + } + } else if (kdba_prologue(&caller_symtab, ar->ret, + ar->start, ar->oldfp, ss, 1, &caller_ar)) { + /* some caller data extracted */ ; + } else if (strcmp(symtab->sym_name, "do_exit") == 0) { + /* non-standard caller, force one argument */ + FORCE_ARG(&caller_ar, 1); + } else if (KDB_DEBUG(AR)) { + kdb_printf("kdb_get_next_ar: caller prologue failed\n"); + } + if (KDB_DEBUG(AR)) { + kdb_printf("kdb_get_next_ar: caller activation record\n"); + kdb_printf(" start=0x%lx end=0x%lx ret=0x%lx" + " oldfp=0x%lx fp=0x%lx\n", + caller_ar.start, caller_ar.end, caller_ar.ret, + caller_ar.oldfp, caller_ar.fp); + kdb_printf(" locals=%d regs=%d args=%d setup=%d\n", + caller_ar.locals, caller_ar.regs, + caller_ar.args, caller_ar.setup); + } + if (caller_ar.start) { + ar->args = KDB_STACK_DIRECTION*(caller_ar.end - caller_ar.start) - + (caller_ar.setup + caller_ar.locals + caller_ar.regs); + if (ar->args < 0) + ar->args = 0; + if (ar->args) { + ar->arg0 = ar->start - + KDB_STACK_DIRECTION*(ar->args - 4); + if (KDB_DEBUG(AR)) { + kdb_printf(" callee arg0=0x%lx args=%d\n", + ar->arg0, ar->args); + } + } + } + } + + return(1); +} + +/* + * kdb_symbol_print + * + * Standard method for printing a symbol name and offset. + * Inputs: + * addr Address to be printed. + * symtab Address of symbol data, if NULL this routine does its + * own lookup. + * punc Punctuation for string, bit field. + * Outputs: + * None. + * Returns: + * Always 0. + * Locking: + * none. + * Remarks: + * The string and its punctuation is only printed if the address + * is inside the kernel, except that the value is always printed + * when requested. + */ + +void +kdb_symbol_print(kdb_machreg_t addr, const kdb_symtab_t *symtab_p, unsigned int punc) +{ + kdb_symtab_t symtab, *symtab_p2; + if (symtab_p) { + symtab_p2 = (kdb_symtab_t *)symtab_p; + } + else { + symtab_p2 = &symtab; + kdbnearsym(addr, symtab_p2); + } + if (symtab_p2->sym_name || (punc & KDB_SP_VALUE)) { + ; /* drop through */ + } + else { + return; + } + if (punc & KDB_SP_SPACEB) { + kdb_printf(" "); + } + if (punc & KDB_SP_VALUE) { + kdb_printf(kdb_machreg_fmt0, addr); + } + if (!symtab_p2->sym_name) { + return; + } + if (punc & KDB_SP_VALUE) { + kdb_printf(" "); + } + if (punc & KDB_SP_PAREN) { + kdb_printf("("); + } + if (strcmp(symtab_p2->mod_name, "kernel")) { + kdb_printf("[%s]", symtab_p2->mod_name); + } + kdb_printf("%s", symtab_p2->sym_name); + if (addr != symtab_p2->sym_start) { + kdb_printf("+0x%lx", addr - symtab_p2->sym_start); + } + if (punc & KDB_SP_SYMSIZE) { + kdb_printf("/0x%lx", symtab_p2->sym_end - symtab_p2->sym_start); + } + if (punc & KDB_SP_PAREN) { + kdb_printf(")"); + } + if (punc & KDB_SP_SPACEA) { + kdb_printf(" "); + } + if (punc & KDB_SP_NEWLINE) { + kdb_printf("\n"); + } +} diff -urN 2.4.0/kdb/modules/Makefile 2.4.0-ikd-1/kdb/modules/Makefile --- 2.4.0/kdb/modules/Makefile Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kdb/modules/Makefile Mon Jan 15 18:40:05 2001 @@ -0,0 +1,15 @@ +# +# Makefile for i386-specific kdb files.. +# +# Copyright 1999, Silicon Graphics Inc. +# +# Written April 1999 by Scott Lurndal at Silicon Graphics, Inc. +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +obj-m := kdbm_vm.o kdbm_pg.o + +include $(TOPDIR)/Rules.make diff -urN 2.4.0/kdb/modules/kdbm_pg.c 2.4.0-ikd-1/kdb/modules/kdbm_pg.c --- 2.4.0/kdb/modules/kdbm_pg.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kdb/modules/kdbm_pg.c Mon Jan 15 18:40:05 2001 @@ -0,0 +1,851 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef KDB_DO_PAGEBUF +#if defined(CONFIG_PAGE_BUF) || defined(CONFIG_PAGE_BUF_MODULE) +#define KDB_DO_PAGEBUF +#define _PAGE_BUF_INTERNAL_ 1 +#include +#endif /* CONFIG_PAGE_BUF || CONFIG_PAGE_BUF_MODULE */ + +/* Standard Linux page stuff */ + +static char *pg_flag_vals[] = { + "PG_locked", "PG_error", "PG_referenced", "PG_uptodate", "", + "PG_decr_after", "", "", + "PG_Slab", "PG_swap_cache", "PG_skip", "PG_swap_entry", + "PG_highmem", "PG_delalloc", "PG_partial", + "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", + "PG_partial", NULL }; + +static char *bh_state_vals[] = { + "Uptodate", "Dirty", "Lock", "Req", "Mapped", "New", + "Protected", NULL }; + +static char *map_flags(unsigned long flags, char *mapping[]) +{ + static char buffer[256]; + int index; + int offset = 12; + + buffer[0] = '\0'; + + for (index = 0; flags && mapping[index]; flags >>= 1, index++) { + if (flags & 1) { + if ((offset + strlen(mapping[index]) + 1) >= 80) { + strcat(buffer, "\n "); + offset = 12; + } else if (offset > 12) { + strcat(buffer, " "); + offset++; + } + strcat(buffer, mapping[index]); + offset += strlen(mapping[index]); + } + } + + return (buffer); +} + +static char *page_flags(unsigned long flags) +{ + return(map_flags(flags, pg_flag_vals)); +} + +static int +kdbm_buffers(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + struct buffer_head bh; + unsigned char *p = (unsigned char *)&bh; + unsigned long addr; + long offset=0; + int nextarg; + int diag; + int i; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i = 0; i < sizeof(struct buffer_head); i++) { + *p++ = kdba_getword(addr+i, 1); + } + + kdb_printf("buffer_head at 0x%lx\n", addr); + kdb_printf(" next 0x%p bno %ld rsec %ld size %d dev 0x%x rdev 0x%x\n", + bh.b_next, bh.b_blocknr, bh.b_rsector, + bh.b_size, bh.b_dev, bh.b_rdev); + kdb_printf(" count %d state 0x%lx [%s] ftime 0x%lx\n", + bh.b_count.counter, bh.b_state, map_flags(bh.b_state, bh_state_vals), + bh.b_flushtime); + kdb_printf(" b_page 0x%p b_this_page 0x%p b_private 0x%p\n", + bh.b_page, bh.b_this_page, bh.b_private); + + return 0; +} + +static int +kdbm_page(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + struct page page; + unsigned char *p = (unsigned char *)&page; + unsigned long addr; + long offset=0; + int nextarg; + int diag; + int i; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + if (addr < PAGE_OFFSET) { + printk("Treating 0x%lx as page index, page at 0x%p\n", + addr, &mem_map[addr]); + addr = (unsigned long) &mem_map[addr]; + } + + for (i = 0; i < sizeof(struct page); i++) { + *p++ = kdba_getword(addr+i, 1); + } + + kdb_printf("struct page at 0x%lx\n", addr); + kdb_printf(" next 0x%p prev 0x%p addr space 0x%p index %lu (offset 0x%x)\n", + page.list.next, page.list.prev, page.mapping, page.index, + (int)(page.index << PAGE_CACHE_SHIFT)); + kdb_printf(" count %d flags %s virtual 0x%p\n", + page.count.counter, page_flags(page.flags), + page.virtual); + kdb_printf(" buffers 0x%p", page.buffers); + +#ifdef KDB_DO_PAGEBUF + { + int mask; + char maskval[33]; + maskval[0] = '\0'; + for (mask = 1, i = 0; i < 32; i++, mask <<= 1) { + strcat(maskval, page.block_map & mask ? "1" : "0"); + } + kdb_printf(" block_map %s\n", maskval); + } +#endif /* KDB_DO_PAGEBUF */ + + return 0; +} + +unsigned long +print_request(unsigned long addr) +{ + struct request rq; + unsigned char *p = (unsigned char *)&rq; + int i; + + for (i = 0; i < sizeof(struct request); i++) { + *p++ = kdba_getword(addr+i, 1); + } + + kdb_printf("struct request at 0x%lx\n", addr); + kdb_printf(" rq_dev 0x%x cmd %d errors %d sector %ld nr_sectors %ld\n", + rq.rq_dev, rq.cmd, rq.errors, rq.sector, + rq.nr_sectors); + + kdb_printf(" hsect %ld hnrsect %ld nrseg %d nrhwseg %d currnrsect %ld seq %d\n", + rq.hard_sector, rq.hard_nr_sectors, + rq.nr_segments, rq.nr_hw_segments, + rq.current_nr_sectors, rq.elevator_sequence); + kdb_printf(" "); +#ifdef STRUCT_REQUEST_HAS_KIOBUF + kdb_printf("kiobuf 0x%p ", rq.kiobuf); +#endif /* STRUCT_REQUEST_HAS_KIOBUF */ + kdb_printf("bh 0x%p bhtail 0x%p req_q 0x%p\n\n", + rq.bh, rq.bhtail, rq.q); + + return (unsigned long) rq.queue.next; +} + +#ifdef KDB_DO_PAGEBUF +static int +kdbm_request(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + long offset=0; + unsigned long addr; + int nextarg; + int diag; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + print_request(addr); + return 0; +} + + +static int +kdbm_rqueue(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + struct request_queue rq; + unsigned char *p = (unsigned char *)&rq; + unsigned long addr, head_addr, next; + long offset=0; + int nextarg; + int diag; + int i; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i = 0; i < sizeof(struct request_queue); i++) { + *p++ = kdba_getword(addr+i, 1); + } + + kdb_printf("struct request_queue at 0x%lx [%s]\n", addr, + rq.plugged ? "plugged" : "running"); + kdb_printf(" read free_list [0x%p, 0x%p]\n", + rq.request_freelist[READ].prev, + rq.request_freelist[READ].next); + kdb_printf(" write free_list [0x%p, 0x%p]\n", + rq.request_freelist[WRITE].prev, + rq.request_freelist[WRITE].next); + + i = 0; + next = (unsigned long)rq.queue_head.next; + head_addr = addr + offsetof(struct request_queue, queue_head); + kdb_printf(" request queue: %s\n", next == head_addr ? + "empty" : ""); + while (next != head_addr) { + i++; + next = print_request(next); + } + + if (i) + kdb_printf("%d requests found\n", i); + + return 0; +} +#endif /* KDB_DO_PAGEBUF */ + + +static void +do_buffer(unsigned long addr) +{ + struct buffer_head bh; + unsigned char *p = (unsigned char *)&bh; + int i; + + for (i = 0; i < sizeof(struct buffer_head); i++) { + *p++ = kdba_getword(addr+i, 1); + } + + kdb_printf("buffer 0x%lx bno %ld [ %s ]\n", addr, bh.b_blocknr, + map_flags(bh.b_state, bh_state_vals)); +} + +static int +kdbm_inode_pages(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + struct inode inode; + struct address_space ap; + unsigned char *p = (unsigned char *)&inode; + unsigned long addr, addr1 = 0; + long offset=0; + int nextarg; + int diag; + int i, j; + + struct list_head *head, *curr; + + if (argc < 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + if (argc == 2) { + nextarg = 2; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr1, + &offset, NULL, regs); + if (diag) + return diag; + kdb_printf("Looking for page index 0x%lx ... \n", addr1); + } + + for (i = 0; i < sizeof(struct inode); i++) { + *p++ = kdba_getword(addr+i, 1); + } + + addr = (unsigned long) inode.i_mapping; + p = (unsigned char *)≈ + + for (i = 0; i < sizeof(struct address_space); i++) { + *p++ = kdba_getword(addr+i, 1); + } + + curr = ap.pages.next; + head = &inode.i_mapping->pages; + + kdb_printf(" page_struct index cnt flags\n"); + + while (curr != head) { + struct page page; + struct list_head curr_struct; + + addr = (unsigned long) list_entry(curr, struct page, list); + p = (unsigned char *)&page; + + for (j = 0; j < sizeof(struct page); j++) + *p++ = kdba_getword(addr+j, 1); + + if (!addr1 || page.index == addr1 || + (addr1 == -1 && (page.flags & ( 1 << PG_locked)))) + { + kdb_printf(" 0x%lx %lu %d 0x%lx ", + addr, page.index, page.count.counter, + page.flags); + if (page.buffers) + do_buffer((unsigned long) page.buffers); + else + kdb_printf("UNMAPPED\n"); + } + + addr = (unsigned long) curr; + p = (unsigned char *)&curr_struct; + for (j = 0; j < sizeof(struct list_head); j++) + *p++ = kdba_getword(addr+j, 1); + + curr = curr_struct.next; + } + + return 0; +} + +static int +kdbm_inode(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + struct inode inode; + unsigned char *p = (unsigned char *)&inode; + unsigned long addr; + unsigned char *iaddr; + long offset=0; + int nextarg; + int diag; + int i; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i = 0; i < sizeof(struct inode); i++) { + *p++ = kdba_getword(addr+i, 1); + } + + kdb_printf("struct inode at 0x%lx\n", addr); + + kdb_printf(" i_ino = %lu i_count = %u i_dev = 0x%x i_size %Ld\n", + inode.i_ino, atomic_read(&inode.i_count), + inode.i_dev, inode.i_size); + + kdb_printf(" i_mode = 0x%x i_nlink = %d i_rdev = 0x%x\n", + inode.i_mode, inode.i_nlink, + inode.i_rdev); + + kdb_printf(" i_hash.nxt = 0x%p i_hash.prv = 0x%p\n", + inode.i_hash.next, inode.i_hash.prev); + + kdb_printf(" i_list.nxt = 0x%p i_list.prv = 0x%p\n", + inode.i_list.next, inode.i_list.prev); + + kdb_printf(" i_dentry.nxt = 0x%p i_dentry.prv = 0x%p\n", + inode.i_dentry.next, + inode.i_dentry.prev); + + kdb_printf(" i_sb = 0x%p i_op = 0x%p i_data = 0x%lx nrpages = %lu\n", + inode.i_sb, inode.i_op, + addr + offsetof(struct inode, i_data), + inode.i_data.nrpages); + + iaddr = (char *)addr; + iaddr += offsetof(struct inode, u); + + kdb_printf(" fs specific info @ 0x%p\n", iaddr); + + return (0); +} + +static int +kdbm_kiobuf(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + struct kiobuf kiobuf; + struct page page; + unsigned char *p = (unsigned char *)&kiobuf; + struct page *page_array[64]; + unsigned long addr; + long offset=0; + int nextarg; + int diag; + int i, j; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; imap_array)) { + p = (char *)page_array; + for (i=0; i < (kiobuf.nr_pages * sizeof(struct page *)); i++) { + *p++ = kdba_getword((kdb_machreg_t)kiobuf.maplist + i, 1); + } + kiobuf.maplist = page_array; + } + kdb_printf(" errno %d", kiobuf.errno); +#ifdef KDB_DO_PAGEBUF + kdb_printf(" pb 0x%p", (page_buf_t *)kiobuf.k_dev_id); +#endif /* KDB_DO_PAGEBUF */ + kdb_printf("\n"); + kdb_printf(" page_struct page_addr cnt flags\n"); + for (i = 0; i < kiobuf.nr_pages; i++) { + addr = (unsigned long) kiobuf.maplist[i]; + p = (unsigned char *)&page; + + for (j = 0; j < sizeof(struct page); j++) { + *p++ = kdba_getword(addr+j, 1); + } + kdb_printf(" 0x%p 0x%p %d 0x%lx\n", + kiobuf.maplist[i], page.virtual, + page.count.counter, page.flags); + } + + return (0); +} + +#ifdef KDB_DO_PAGEBUF + +/* pagebuf stuff */ + +static char *pb_flag_vals[] = { + "READ", "WRITE", "MAPPED", "PARTIAL", + "ASYNC", "NONE", "DELWRI", "FREED", "SYNC", + "MAPPABLE", "FS_RESERVED_1", "FS_RESERVED_2", "RELEASE", + "LOCK", "TRYLOCK", "ALLOCATE", "FILE_ALLOCATE", "DONT_BLOCK", + "DIRECT", "LOCKABLE", "NEXT_KEY", "ENTER_PAGES", + "ALL_PAGES_MAPPED", "SOME_INVALID_PAGES", "ADDR_ALLOCATED", + "MEM_ALLOCATED", "GRIO", "FORCEIO", + NULL }; + +static char *pbm_flag_vals[] = { + "EOF", "HOLE", "DELAY", "FLUSH_OVERLAPS", + "READAHEAD", "UNWRITTEN", "DONTALLOC", "NEW", + NULL }; + +static char *pb_flags(page_buf_flags_t pb_flag) +{ + return(map_flags((unsigned long) pb_flag, pb_flag_vals)); +} + +static int +kdbm_pb_flags(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + unsigned long flags; + int diag; + + if (argc != 1) + return KDB_ARGCOUNT; + + diag = kdbgetularg(argv[1], &flags); + if (diag) + return diag; + + kdb_printf("pb flags 0x%lx = %s\n", flags, pb_flags(flags)); + + return 0; +} + +static int +kdbm_pb(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + page_buf_private_t bp; + unsigned char *p = (unsigned char *)&bp; + unsigned long addr; + long offset=0; + int nextarg; + int diag; + int i; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; i + +#define EV_SIZE (sizeof(event_names)/sizeof(char *)) + +void +pb_trace_core( + int match, + char *event_match, + unsigned long long offset, + long long mask) +{ + extern struct pagebuf_trace_buf pb_trace; + int i, total, end; + pagebuf_trace_t *trace; + char *event; + char value[10]; + + end = pb_trace.start - 1; + if (end < 0) + end = PB_TRACE_BUFSIZE - 1; + + if (match && (match < PB_TRACE_BUFSIZE)) { + for (i = pb_trace.start, total = 0; i != end; i = CIRC_INC(i)) { + trace = &pb_trace.buf[i]; + if (trace->pb == 0) + continue; + total++; + } + total = total - match; + for (i = pb_trace.start; i != end && total; i = CIRC_INC(i)) { + trace = &pb_trace.buf[i]; + if (trace->pb == 0) + continue; + total--; + } + match = 0; + } else + i = pb_trace.start; + for ( ; i != end; i = CIRC_INC(i)) { + trace = &pb_trace.buf[i]; + + if (offset) { + if ((trace->offset & ~mask) != offset) + continue; + } + + if (trace->pb == 0) + continue; + + if ((match != 0) && (trace->pb != match)) + continue; + + if ((trace->event < EV_SIZE) && event_names[trace->event]) { + event = event_names[trace->event]; + } else if (trace->event == 1000) { + event = (char *)trace->misc; + } else { + event = value; + sprintf(value, "%8d", trace->event); + } + + if (event_match && strcmp(event, event_match)) { + continue; + } + + + kdb_printf("pb 0x%lx [%s] (hold %u lock %d) misc 0x%p", + trace->pb, event, + trace->hold, trace->lock_value, + trace->misc); + kdb_symbol_print((unsigned int)trace->ra, NULL, + KDB_SP_SPACEB|KDB_SP_PAREN|KDB_SP_NEWLINE); + kdb_printf(" offset 0x%Lx size 0x%x task 0x%p\n", + trace->offset, trace->size, trace->task); + kdb_printf(" flags: %s\n", + pb_flags(trace->flags)); + } +} + + +static int +kdbm_pbtrace_offset(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + long mask = 0; + unsigned long offset = 0; + int diag; + + if (argc > 2) + return KDB_ARGCOUNT; + + if (argc > 0) { + diag = kdbgetularg(argv[1], &offset); + if (diag) + return diag; + } + + if (argc > 1) { + diag = kdbgetularg(argv[1], &mask); + if (diag) + return diag; + } + + pb_trace_core(0, NULL, (unsigned long long)offset, + (long long)mask); /* sign extent mask */ + return 0; +} + +static int +kdbm_pbtrace(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + unsigned long match = 0; + int diag; + char *event_match = NULL; + + if (argc > 1) + return KDB_ARGCOUNT; + + if (argc == 1) { + if ((argv[1][0] >= 'a') && (argv[1][0] <= 'z')) { + event_match = (char *)argv[1]; + printk("event match on \"%s\"\n", event_match); + argc = 0; + } else { + diag = kdbgetularg(argv[1], &match); + if (diag) + return diag; + } + } + + pb_trace_core(match, event_match, 0LL, 0LL); + return 0; +} + +#else /* PAGEBUF_TRACE */ +static int +kdbm_pbtrace(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + kdb_printf("pagebuf tracing not enabled\n"); + + return 0; +} +#endif /* PAGEBUF_TRACE */ +#endif /* KDB_DO_PAGEBUF */ + +int +init_module(void) +{ + kdb_register("kiobuf", kdbm_kiobuf, "", "Display kiobuf", 0); + kdb_register("page", kdbm_page, "", "Display page", 0); + kdb_register("inode", kdbm_inode, "", "Display inode", 0); + kdb_register("bh", kdbm_buffers, "", "Display buffer", 0); + kdb_register("inode_pages", kdbm_inode_pages, "", "Display pages in an inode", 0); +#ifdef KDB_DO_PAGEBUF + kdb_register("pb", kdbm_pb, "", "Display page_buf_t", 0); + kdb_register("pbflags", kdbm_pb_flags, "", "Display page buf flags", 0); + kdb_register("pbiodesc", kdbm_pbiodesc, "", "Display I/O Descriptor", 0); + kdb_register("pbmap", kdbm_pbmap, "", "Display Bmap", 0); + kdb_register("pbtrace", kdbm_pbtrace, "|", "page_buf_t trace", 0); +#ifdef PAGEBUF_TRACE + kdb_register("pboffset", kdbm_pbtrace_offset, " []", "page_buf_t trace", 0); +#endif + kdb_register("req", kdbm_request, "", "dump request struct", 0); + kdb_register("rqueue", kdbm_rqueue, "", "dump request queue", 0); + +#endif /* KDB_DO_PAGEBUF */ + + return 0; +} + + +void +cleanup_module(void) +{ + kdb_unregister("kiobuf"); + kdb_unregister("page"); + kdb_unregister("inode"); + kdb_unregister("bh"); + kdb_unregister("inode_pages"); + kdb_unregister("pb"); + kdb_unregister("pbflags"); + kdb_unregister("pbmap"); + kdb_unregister("pbiodesc"); + kdb_unregister("pbtrace"); +#ifdef PAGEBUF_TRACE + kdb_unregister("pboffset"); +#endif + kdb_unregister("req"); + kdb_unregister("rqueue"); +} diff -urN 2.4.0/kdb/modules/kdbm_vm.c 2.4.0-ikd-1/kdb/modules/kdbm_vm.c --- 2.4.0/kdb/modules/kdbm_vm.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kdb/modules/kdbm_vm.c Mon Jan 15 18:40:05 2001 @@ -0,0 +1,583 @@ +#include +#include +#include +#include +#include +#include + +#include <../drivers/scsi/scsi.h> +#include <../drivers/scsi/hosts.h> + +/* The request printing worked under 2.3.42 but not 2.3.99-pre2. + * Feel free to rework it for 2.3.99-pre2 and submit a patch to SGI. + * Look for DO_REQUEST_PRINTING, remove the #ifdef and this comment + * when you get it working. Keith Owens + */ + +struct __vmflags { + unsigned long mask; + char *name; +} vmflags[] = { + { VM_READ, "READ" }, + { VM_WRITE, "WRITE" }, + { VM_EXEC, "EXEC" }, + { VM_SHARED, "SHARED" }, + { VM_MAYREAD, "MAYREAD" }, + { VM_MAYWRITE, "MAYWRITE" }, + { VM_MAYEXEC, "MAYEXEC" }, + { VM_MAYSHARE, "MAYSHARE" }, + { VM_GROWSDOWN, "GROWSDOWN" }, + { VM_GROWSUP, "GROWSUP" }, + { VM_SHM, "SHM" }, + { VM_DENYWRITE, "DENYWRITE" }, + { VM_EXECUTABLE, "EXECUTABLE" }, + { VM_LOCKED, "LOCKED" }, + { VM_IO , "IO " }, + { 0, "" } +}; + +static int +kdbm_vm(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + struct vm_area_struct vp; + unsigned char *bp = (unsigned char *)&vp; + unsigned long addr; + long offset=0; + int nextarg; + int diag; + struct __vmflags *tp; + int i; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; imask; tp++) { + if (vp.vm_flags & tp->mask) { + kdb_printf("%s ", tp->name); + } + } + kdb_printf("\n"); + + return 0; +} + +static int +kdbm_fp(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + struct file f; + unsigned char *fp = (unsigned char *)&f; + struct inode i; + unsigned char *ip = (unsigned char *)&i; + struct dentry d; + unsigned char *dp = (unsigned char *)&d; +#if 0 + unsigned char namebuf[80]; + unsigned char *np = namebuf; +#endif + int nextarg; + unsigned long addr; + unsigned long filpaddr; + long offset; + int diag; + int j; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + filpaddr = addr; + for (j=0; j\n", + d.d_name.len, d.d_name.name); + + kdb_printf(" d_count = %d d_flags = 0x%x d_inode = 0x%p\n", + atomic_read(&d.d_count), d.d_flags, d.d_inode); + +#ifdef FIXME_for_2_4_0_test1 + kdb_printf(" d_parent = 0x%p d_mounts = 0x%p d_covers = 0x%p\n", + d.d_parent, d.d_mounts, d.d_covers); +#endif + + kdb_printf(" d_hash.nxt = 0x%p d_hash.prv = 0x%p\n", + d.d_hash.next, d.d_hash.prev); + + kdb_printf(" d_lru.nxt = 0x%p d_lru.prv = 0x%p\n", + d.d_lru.next, d.d_lru.prev); + + kdb_printf(" d_child.nxt = 0x%p d_child.prv = 0x%p\n", + d.d_child.next, d.d_child.prev); + + kdb_printf(" d_subdirs.nxt = 0x%p d_subdirs.prv = 0x%p\n", + d.d_subdirs.next, d.d_subdirs.prev); + + kdb_printf(" d_alias.nxt = 0x%p d_alias.prv = 0x%p\n", + d.d_alias.next, d.d_alias.prev); + + kdb_printf(" d_op = 0x%p d_sb = 0x%p\n\n", + d.d_op, d.d_sb); + + + kdb_printf("\nInode Entry at 0x%p\n", d.d_inode); + + kdb_printf(" i_mode = 0x%x i_nlink = %d i_rdev = 0x%x\n", + i.i_mode, i.i_nlink, i.i_rdev); + + kdb_printf(" i_ino = %ld i_count = %d i_dev = 0x%x\n", + i.i_ino, atomic_read(&i.i_count), i.i_dev); + + kdb_printf(" i_hash.nxt = 0x%p i_hash.prv = 0x%p\n", + i.i_hash.next, i.i_hash.prev); + + kdb_printf(" i_list.nxt = 0x%p i_list.prv = 0x%p\n", + i.i_list.next, i.i_list.prev); + + kdb_printf(" i_dentry.nxt = 0x%p i_dentry.prv = 0x%p\n", + i.i_dentry.next, i.i_dentry.prev); + + return 0; +} + +static int +kdbm_dentry(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + struct dentry d; + unsigned char *dp = (unsigned char *)&d; +#if 0 + unsigned char namebuf[80]; + unsigned char *np = namebuf; +#endif + int nextarg; + unsigned long addr; + unsigned long dentryaddr; + long offset; + int diag; + int j; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + dentryaddr = addr; + + for (j=0; j\n", + d.d_name.len, d.d_name.name); +#if 0 + kdb_printf(" d_name: %s\n", namebuf); +#endif + + kdb_printf(" d_count = %d d_flags = 0x%x d_inode = 0x%p\n", + atomic_read(&d.d_count), d.d_flags, d.d_inode); + +#ifdef FIXME_for_2_4_0_test1 + kdb_printf(" d_parent = 0x%p d_mounts = 0x%p d_covers = 0x%p\n", + d.d_parent, d.d_mounts, d.d_covers); +#endif + + kdb_printf(" d_hash.nxt = 0x%p d_hash.prv = 0x%p\n", + d.d_hash.next, d.d_hash.prev); + + kdb_printf(" d_lru.nxt = 0x%p d_lru.prv = 0x%p\n", + d.d_lru.next, d.d_lru.prev); + + kdb_printf(" d_child.nxt = 0x%p d_child.prv = 0x%p\n", + d.d_child.next, d.d_child.prev); + + kdb_printf(" d_subdirs.nxt = 0x%p d_subdirs.prv = 0x%p\n", + d.d_subdirs.next, d.d_subdirs.prev); + + kdb_printf(" d_alias.nxt = 0x%p d_alias.prv = 0x%p\n", + d.d_alias.next, d.d_alias.prev); + + kdb_printf(" d_op = 0x%p d_sb = 0x%p\n\n", + d.d_op, d.d_sb); + + return 0; +} + +static int +kdbm_sh(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + int diag; + int nextarg; + unsigned long addr; + long offset =0L; + struct Scsi_Host sh; + unsigned char *shp = (unsigned char *)&sh; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; i < sizeof(struct Scsi_Host); i++) + *shp++ = kdba_getword(addr+i, 1); + + kdb_printf("Scsi_Host at 0x%lx\n", addr); + kdb_printf("next = 0x%p host_queue = 0x%p\n", + sh.next, sh.host_queue); + kdb_printf("ehandler = 0x%p eh_wait = 0x%p en_notify = 0x%p eh_action = 0x%p\n", + sh.ehandler, sh.eh_wait, sh.eh_notify, sh.eh_action); + kdb_printf("eh_active = 0x%d host_wait = 0x%p hostt = 0x%p host_busy = %d\n", + sh.eh_active, &sh.host_wait, sh.hostt, sh.host_active.counter); + kdb_printf("host_failed = %d extra_bytes = %d host_no = %d resetting = %d\n", + sh.host_failed, sh.extra_bytes, sh.host_no, sh.resetting); + kdb_printf("max id/lun/channel = [%d/%d/%d] this_id = %d\n", + sh.max_id, sh.max_lun, sh.max_channel, sh.this_id); + kdb_printf("can_queue = %d cmd_per_lun = %d sg_tablesize = %d u_isa_dma = %d\n", + sh.can_queue, sh.cmd_per_lun, sh.sg_tablesize, sh.unchecked_isa_dma); + kdb_printf("host_blocked = %d reverse_ordering = %d \n", + sh.host_blocked, sh.reverse_ordering); + + return 0; +} + +static int +kdbm_sd(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + int diag; + int nextarg; + unsigned long addr; + long offset =0L; + struct scsi_device sd; + unsigned char *sdp = (unsigned char *)&sd; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; i < sizeof(struct scsi_device); i++) + *sdp++ = kdba_getword(addr+i, 1); + + kdb_printf("scsi_device at 0x%lx\n", addr); + kdb_printf("next = 0x%p prev = 0x%p host = 0x%p\n", + sd.next, sd.prev, sd.host); + kdb_printf("device_busy = %d device_queue 0x%p\n", + sd.device_busy, sd.device_queue); + kdb_printf("id/lun/chan = [%d/%d/%d] single_lun = %d device_blocked = %d\n", + sd.id, sd.lun, sd.channel, sd.single_lun, sd.device_blocked); + kdb_printf("queue_depth = %d current_tag = %d scsi_level = %d\n", + sd.queue_depth, sd.current_tag, sd.scsi_level); + kdb_printf("%8.8s %16.16s %4.4s\n", sd.vendor, sd.model, sd.rev); + + return 0; +} + +static char * +str_rq_status(int rq_status) +{ + switch (rq_status) { + case RQ_INACTIVE: + return "RQ_INACTIVE"; + case RQ_ACTIVE: + return "RQ_ACTIVE"; + case RQ_SCSI_BUSY: + return "RQ_SCSI_BUSY"; + case RQ_SCSI_DONE: + return "RQ_SCSI_DONE"; + case RQ_SCSI_DISCONNECTING: + return "RQ_SCSI_DISCONNECTING"; + default: + return "UNKNOWN"; + } +} + +#ifdef DO_REQUEST_PRINTING +static char * +rq_cmds[] = { + "READ", + "WRITE", + "READA", + "", + "SPECIAL", + "WRITERAW" +}; +static unsigned int num_rq_cmds = sizeof(rq_cmds) / sizeof(char *); + +static void +show_request(unsigned long addr, struct request *rq) +{ + kdb_printf("struct request at 0x%lx\n", addr); + kdb_printf("cmd: %s\n", + ((rq->cmd >=0) && (rq->cmd <= num_rq_cmds))? + rq_cmds[rq->cmd]: ""); + kdb_printf("rq_status = %s rq_dev = [%d/%d] errors = %d\n", + str_rq_status(rq->rq_status), + MAJOR(rq->rq_dev), + MINOR(rq->rq_dev), + rq->errors); + kdb_printf("sector = %d nr_sectors = %d\n", + rq->sector, rq->nr_sectors); + kdb_printf("nr_segments = %d nr_hw_segments = %d current_nr_sectors = %d\n", + rq->nr_segments, rq->nr_hw_segments, rq->current_nr_sectors); + kdb_printf("special = 0x%lx buffer = 0x%lx semaphore = 0x%lx\n", + rq->special, rq->buffer, rq->sem); + kdb_printf("bh = 0x%lx bhtail = 0x%lx next = 0x%lx\n", + rq->bh, rq->bhtail, rq->next); +} +#endif /* DO_REQUEST_PRINTING */ + +static int +kdbm_sc(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + int diag; + int nextarg; + unsigned long addr; + long offset =0L; + struct scsi_cmnd sc; + unsigned char *scp = (unsigned char *)≻ + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; i < sizeof(struct scsi_cmnd); i++) + *scp++ = kdba_getword(addr+i, 1); + + kdb_printf("scsi_cmnd at 0x%lx\n", addr); + kdb_printf("host = 0x%p state = %d owner = %d device = 0x%p\nb", + sc.host, sc.state, sc.owner, sc.device); + kdb_printf("next = 0x%p reset_chain = 0x%p eh_state = %d done = 0x%p\n", + sc.next, sc.reset_chain, sc.eh_state, sc.done); + kdb_printf("serial_number = %ld serial_num_at_to = %ld retries = %d timeout = %d\n", + sc.serial_number, sc.serial_number_at_timeout, sc.retries, sc.timeout); + kdb_printf("id/lun/cmnd = [%d/%d/%d] cmd_len = %d old_cmd_len = %d\n", + sc.target, sc.lun, sc.channel, sc.cmd_len, sc.old_cmd_len); + kdb_printf("cmnd = [%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x]\n", + sc.cmnd[0], sc.cmnd[1], sc.cmnd[2], sc.cmnd[3], sc.cmnd[4], + sc.cmnd[5], sc.cmnd[6], sc.cmnd[7], sc.cmnd[8], sc.cmnd[9], + sc.cmnd[10], sc.cmnd[11]); + kdb_printf("data_cmnd = [%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x]\n", + sc.data_cmnd[0], sc.data_cmnd[1], sc.data_cmnd[2], sc.data_cmnd[3], sc.data_cmnd[4], + sc.data_cmnd[5], sc.data_cmnd[6], sc.data_cmnd[7], sc.data_cmnd[8], sc.data_cmnd[9], + sc.data_cmnd[10], sc.data_cmnd[11]); + kdb_printf("request_buffer = 0x%p bh_next = 0x%p request_bufflen = %d\n", + sc.request_buffer, sc.bh_next, sc.request_bufflen); + kdb_printf("use_sg = %d old_use_sg = %d sglist_len = %d abore_reason = %d\n", + sc.use_sg, sc.old_use_sg, sc.sglist_len, sc.abort_reason); + kdb_printf("bufflen = %d buffer = 0x%p underflow = %d transfersize = %d\n", + sc.bufflen, sc.buffer, sc.underflow, sc.transfersize); + kdb_printf("tag = %d pid = %ld\n", + sc.tag, sc.pid); + kdb_printf("request struct\n"); + kdb_printf("rq_status = %s rq_dev = [%d/%d] errors = %d cmd = %d\n", + str_rq_status(sc.request.rq_status), + MAJOR(sc.request.rq_dev), + MINOR(sc.request.rq_dev), sc.request.cmd, + sc.request.errors); + kdb_printf("sector = %ld nr_sectors = %ld current_nr_sectors = %ld\n", + sc.request.sector, sc.request.nr_sectors, sc.request.current_nr_sectors); + kdb_printf("buffer = 0x%p sem = 0x%p bh = 0x%p bhtail = 0x%p\n", + sc.request.buffer, sc.request.sem, sc.request.bh, sc.request.bhtail); +#ifdef DO_REQUEST_PRINTING + show_request(0, &sc.request); +#endif /* DO_REQUEST_PRINTING */ + + return 0; +} + +#ifdef DO_REQUEST_PRINTING +static int +kdbm_req(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + int diag; + int nextarg; + unsigned long addr; + long offset =0L; + struct request req; + unsigned char *rqp = (unsigned char *)&req; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; i < sizeof(struct request); i++) + *rqp++ = kdba_getword(addr+i, 1); + + show_request(addr, &req); + + return 0; +} + +static int +kdbm_rq(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + int diag; + int nextarg; + unsigned long addr; + long offset =0L; + request_queue_t rq; + struct request req; + unsigned char *rqp = (unsigned char *)&rq; + kdb_symtab_t symtab1, symtab2; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; i < sizeof(request_queue_t); i++) + *rqp++ = kdba_getword(addr+i, 1); + kdb_printf("request queue @ 0x%lx\n", addr); + kdbnearsym((unsigned long)rq.request_fn, &symtab1), + kdbnearsym((unsigned long)rq.merge_fn), &symtab2); + kdb_printf("current_request = 0x%p request_fn = '%s'\n merge_fn = '%s'", + rq.current_request, + symtab1.sym_name, + symtab2.sym_name); + kdbnearsym((unsigned long)rq.merge_requests_fn, &symtab1), + kdb_printf("merge_requests_fn = '%s'\n queuedata= 0x%p plugged = %d\n", + symtab1.sym_name, + rq.queuedata, rq.plugged); + kdb_printf("head_active = %d use_plug = %d\n", + rq.head_active, rq.use_plug); + kdb_printf("queue:\n"); + addr = (unsigned long)rq.current_request; + while (addr) { + rqp = (unsigned char *)&req; + for (i=0; i < sizeof(struct request); i++) + *rqp++ = kdba_getword(addr+i, 1); + show_request(addr, &req); + addr = req.next; + } + + return 0; +} +#endif /* DO_REQUEST_PRINTING */ + +int +init_module(void) +{ + kdb_register("vm", kdbm_vm, "", "Display vm_area_struct", 0); + kdb_register("dentry", kdbm_dentry, "", "Display interesting dentry stuff", 0); + kdb_register("filp", kdbm_fp, "", "Display interesting filp stuff", 0); + kdb_register("sh", kdbm_sh, "", "Show scsi_host", 0); + kdb_register("sd", kdbm_sd, "", "Show scsi_device", 0); + kdb_register("sc", kdbm_sc, "", "Show scsi_cmnd", 0); +#ifdef DO_REQUEST_PRINTING + kdb_register("req", kdbm_req, "", "Show struct request", 0); + kdb_register("rq", kdbm_rq, "", "Show request queue", 0); +#endif /* DO_REQUEST_PRINTING */ + + return 0; +} + +void +cleanup_module(void) +{ + kdb_unregister("vm"); + kdb_unregister("dentry"); + kdb_unregister("filp"); + kdb_unregister("sh"); + kdb_unregister("sd"); + kdb_unregister("sc"); +#ifdef DO_REQUEST_PRINTING + kdb_unregister("req"); + kdb_unregister("rq"); +#endif /* DO_REQUEST_PRINTING */ +} diff -urN 2.4.0/kernel/Makefile 2.4.0-ikd-1/kernel/Makefile --- 2.4.0/kernel/Makefile Tue Jan 2 17:41:22 2001 +++ 2.4.0-ikd-1/kernel/Makefile Mon Jan 15 18:40:05 2001 @@ -20,6 +20,8 @@ obj-$(CONFIG_MODULES) += ksyms.o obj-$(CONFIG_PM) += pm.o +obj-$(CONFIG_KALLSYMS) += kallsyms.o + ifneq ($(CONFIG_IA64),y) # According to Alan Modra , the -fno-omit-frame-pointer is # needed for x86 only. Why this used to be enabled for all architectures is beyond diff -urN 2.4.0/kernel/debug/Config.in 2.4.0-ikd-1/kernel/debug/Config.in --- 2.4.0/kernel/debug/Config.in Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kernel/debug/Config.in Mon Jan 15 18:40:05 2001 @@ -0,0 +1,46 @@ +# +# Common kernel debugging configuration. arch specific debugging facilities +# are in arch/xxx/config.in. +# + bool 'Kernel debugging support' CONFIG_KERNEL_DEBUGGING n + if [ "$CONFIG_KERNEL_DEBUGGING" = "y" ]; then + comment 'note: enabling either kernel tracer or stack meter will' + comment ' of necessity disable the nmi-watchdog.' + if [ "$CONFIG_NOHIGHMEM" = "y" ]; then + bool ' GFP poison' CONFIG_GFP_POISON n + bool ' SLAB poison' CONFIG_SLAB_POISON n + fi + bool ' Semphore deadlock detector' CONFIG_SEMAPHORE_DEADLOCK n + bool ' Debug kernel stack overflows' CONFIG_DEBUG_KSTACK n + if [ "$CONFIG_DEBUG_KSTACK" = "y" ]; then + int ' Stack threshold' CONFIG_KSTACK_THRESHOLD 500 + fi + bool ' Kernel Stack Meter' CONFIG_KSTACK_METER n + bool ' Detect software lockups' CONFIG_DEBUG_SOFTLOCKUP n + if [ "$CONFIG_DEBUG_SOFTLOCKUP" = "y" ]; then + int ' Deadlock threshold' CONFIG_SOFTLOCKUP_THRESHOLD 100000000 0 2147483647 + fi + bool ' GCC profiling support' CONFIG_PROFILE_GCC n + bool ' Enable kernel tracer' CONFIG_TRACE n + if [ "$CONFIG_TRACE" = "y" ]; then + int ' Trace ringbuffer size' CONFIG_TRACE_SIZE 16384 + int ' Emergency trace length' CONFIG_ETRACE_LENGTH 30 + bool ' Trace timestamps' CONFIG_TRACE_TIMESTAMP n + if [ "$CONFIG_TRACE_TIMESTAMP" = "y" ]; then + bool ' Truncate timestamp' CONFIG_TRACE_TRUNCTIME n + fi + bool ' Process ID' CONFIG_TRACE_PID n + bool ' Cpu ID' CONFIG_TRACE_CPU n + fi + # CONFIG_DEBUG_MCOUNT is "y" iff an option requires calls to mcount(). + if [ "$CONFIG_DEBUG_KSTACK" = "y" -o \ + "$CONFIG_DEBUG_SOFTLOCKUP" = "y" -o \ + "$CONFIG_KSTACK_METER" = "y" -o \ + "$CONFIG_TRACE" = "y" -o \ + "$CONFIG_PRINT_EIP" = "y" -o \ + "$CONFIG_PROFILE_GCC" = "y" ]; then + define_bool CONFIG_DEBUG_MCOUNT y + else + define_bool CONFIG_DEBUG_MCOUNT n + fi + fi diff -urN 2.4.0/kernel/debug/Makefile 2.4.0-ikd-1/kernel/debug/Makefile --- 2.4.0/kernel/debug/Makefile Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kernel/debug/Makefile Mon Jan 15 18:40:05 2001 @@ -0,0 +1,20 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +O_TARGET := debug.o +obj-y = profiler.o +export-objs = profiler.o + +obj-$(CONFIG_MEMLEAK) += memleak.o +obj-$(CONFIG_LOCKMETER) += lockmeter.o + +# Must turn off profiling for the profiler. +override CFLAGS := $(CFLAGS:%-pg=%) + +include $(TOPDIR)/Rules.make diff -urN 2.4.0/kernel/debug/lockmeter.c 2.4.0-ikd-1/kernel/debug/lockmeter.c --- 2.4.0/kernel/debug/lockmeter.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kernel/debug/lockmeter.c Mon Jan 15 18:40:05 2001 @@ -0,0 +1,479 @@ +/* + * Copyright (C) 1999,2000 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.c by Jack Steiner (steiner@sgi.com) + * + * Modified by Ray Bryant (raybry@us.ibm.com) Feb-Mar 2000 + * Changes Copyright (C) 2000 IBM, Inc. + * Added cache of index in spinlock_t to improve efficiency + * of "hold" time reporting for spinlocks + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ASSERT(cond) +#define bzero(loc,size) memset(loc,0,size) + +/*<---------------------------------------------------*/ +/* lockmeter.c */ +/*>---------------------------------------------------*/ + +static lstat_control_t lstat_control = {LSTAT_OFF, SPIN_LOCK_UNLOCKED}; + +extern char _etext; + +static ushort lstat_make_dir_entry(void *, void *); + + +/* + * lstat_lookup + * + * Given a RA, locate the directory entry for the lock. + */ +static ushort +lstat_lookup( + void *lock_ptr, + void *caller_ra) +{ + ushort index; + lstat_directory_entry_t *dirp; + + dirp = lstat_control.dir; + + index = lstat_control.hashtab[DIRHASH(caller_ra)]; + while (dirp[index].caller_ra != caller_ra) { + if (index == 0) { + return(lstat_make_dir_entry(lock_ptr, caller_ra)); + } + index = dirp[index].next_stat_index; + } + + if (dirp[index].lock_ptr != NULL && + dirp[index].lock_ptr != lock_ptr) { + dirp[index].lock_ptr = NULL; + } + + return(index); +} + + +/* + * lstat_make_dir_entry + * Called to add a new lock to the lock directory. + */ +static ushort +lstat_make_dir_entry( + void *lock_ptr, + void *caller_ra) +{ + lstat_directory_entry_t *dirp; + ushort index, hindex; + unsigned long flags; + + /* lock the table without recursively reentering this metering code */ + do { save_and_cli(flags); + nonmetered_spin_lock(&lstat_control.directory_lock); } while(0); + + hindex = DIRHASH(caller_ra); + index = lstat_control.hashtab[hindex]; + dirp = lstat_control.dir; + while (index && dirp[index].caller_ra != caller_ra) + index = dirp[index].next_stat_index; + + if (index == 0) { + if(lstat_control.next_free_dir_index < LSTAT_MAX_STAT_INDEX) { + index = lstat_control.next_free_dir_index++; + lstat_control.dir[index].caller_ra = caller_ra; + lstat_control.dir[index].lock_ptr = lock_ptr; + lstat_control.dir[index].next_stat_index = lstat_control.hashtab[hindex]; + lstat_control.hashtab[hindex] = index; + } + /* NOTYET + * } else lstat_control.dir_overflow++; + */ + } + + do { nonmetered_spin_unlock(&lstat_control.directory_lock); + restore_flags(flags);} while(0); + return(index); +} + +int +lstat_update ( + void *lock_ptr, + void *caller_ra, + int action) +{ + int index; + int cpu; + + ASSERT(action < LSTAT_ACT_MAX_VALUES); + + if (lstat_control.state == LSTAT_OFF) { + return(0); + } + + index = lstat_lookup(lock_ptr, caller_ra); + cpu = smp_processor_id(); + (*lstat_control.counts[cpu])[index].count[action]++; + (*lstat_control.counts[cpu])[index].acquire_time = get_cycles(); + + return(index); +} + +int +lstat_update_time ( + void *lock_ptr, + void *caller_ra, + int action, + uint32_t ticks) +{ + ushort index; + int cpu; + + ASSERT(action < LSTAT_ACT_MAX_VALUES); + + if (lstat_control.state == LSTAT_OFF) { + return(0); + } + + index = lstat_lookup(lock_ptr, caller_ra); + cpu = smp_processor_id(); + (*lstat_control.counts[cpu])[index].count[action]++; + (*lstat_control.counts[cpu])[index].cum_wait_ticks += (uint64_t)ticks; + if ((*lstat_control.counts[cpu])[index].max_wait_ticks < ticks) + (*lstat_control.counts[cpu])[index].max_wait_ticks = ticks; + + (*lstat_control.counts[cpu])[index].acquire_time = get_cycles(); + + return(index); +} + +void +lstat_initialize(void) +{ + spin_lock_init(&lstat_control.control_lock); +} + +void _spin_lock_(spinlock_t *lock_ptr) +{ + if (lstat_control.state == LSTAT_OFF) { + nonmetered_spin_lock(lock_ptr); /* do the real lock */ + PUT_INDEX(lock_ptr->lock,0); /* avoid timing window */ + } else { + + void *this_pc = LSTAT_RA(0); + int index; + + if (nonmetered_spin_trylock(lock_ptr)) { + index = lstat_update(lock_ptr, this_pc, LSTAT_ACT_NO_WAIT); + } else { + uint32_t start_cycles = get_cycles(); + nonmetered_spin_lock(lock_ptr); /* do the real lock */ + index = lstat_update_time(lock_ptr, this_pc, LSTAT_ACT_SPIN, + get_cycles() - start_cycles); + } + /* cache the index in the lock itself for use in spin unlock */ + PUT_INDEX(lock_ptr->lock,index); + } +} + +int _spin_trylock_(spinlock_t *lock_ptr) +{ + int retval; + int index; + void *this_pc = LSTAT_RA(0); + + if ((retval = nonmetered_spin_trylock(lock_ptr))) { + index = lstat_update(lock_ptr, this_pc, LSTAT_ACT_NO_WAIT); + /* cache the index in the lock itself for use in spin unlock */ + PUT_INDEX(lock_ptr->lock,index); + } else { + lstat_update(lock_ptr, this_pc, LSTAT_ACT_REJECT); + } + + return retval; +} + +void _spin_unlock_(spinlock_t *lock_ptr) +{ + if (lstat_control.state != LSTAT_OFF) { + int index = GET_INDEX(lock_ptr->lock); + /* + * If statistics were turned off when we set the lock, + * then the index can be zero. If that is the case, + * then collect no stats on this call. + */ + if (index > 0) { + uint32_t hold_time; + int cpu = smp_processor_id(); + hold_time = get_cycles() - (*lstat_control.counts[cpu])[index].acquire_time; + (*lstat_control.counts[cpu])[index].cum_hold_ticks += (uint64_t)hold_time; + if ((*lstat_control.counts[cpu])[index].max_hold_ticks < hold_time) + (*lstat_control.counts[cpu])[index].max_hold_ticks = hold_time; + } + } + + /* make sure we don't have a stale index value cached */ + PUT_INDEX(lock_ptr->lock,0); + nonmetered_spin_unlock(lock_ptr); /* do the real unlock */ +} + +void _read_lock_(rwlock_t *rwlock_ptr) +{ + void *this_pc = LSTAT_RA(LSTAT_RA_RWSPIN); + uint32_t start_cycles; + + if (nonmetered_read_trylock(rwlock_ptr)) { + /* + * We have decremented the lock to count a new reader, + * and have confirmed that no writer has it locked. + */ + lstat_update((void *)rwlock_ptr, this_pc, LSTAT_ACT_NO_WAIT); + return; /* do an explicit return here, to make it obvious */ + } + /* If we get here, then we could not quickly grab the read lock */ + + start_cycles = get_cycles(); /* start counting the wait time */ + + /* Now spin until read_lock is successful */ + while (!nonmetered_read_trylock(rwlock_ptr)); + + lstat_update_time((void *)rwlock_ptr, this_pc, LSTAT_ACT_SPIN, + get_cycles() - start_cycles); +} + +void _write_lock_(rwlock_t *rwlock_ptr) +{ + uint32_t start_cycles; + void *this_pc = LSTAT_RA(LSTAT_RA_RWSPIN); + + if (nonmetered_write_trylock(rwlock_ptr)) { + /* We acquired the lock on the first try */ + lstat_update((void *)rwlock_ptr, this_pc, LSTAT_ACT_NO_WAIT); + + return; /* do an explicit return here, to make it obvious */ + } + /* If we get here, then we could not quickly grab the write lock */ + start_cycles = get_cycles(); /* start counting the wait time */ + + /* Now perform the normal spin-until-write-is-successful */ + while (!nonmetered_write_trylock(rwlock_ptr)); + + lstat_update_time((void *)rwlock_ptr, this_pc, LSTAT_ACT_SPIN, + get_cycles() - start_cycles); +} + +int _write_trylock_(rwlock_t *rwlock_ptr) +{ + int retval; + void *this_pc = LSTAT_RA(LSTAT_RA_RWSPIN); + + if ((retval = nonmetered_write_trylock(rwlock_ptr))) { + lstat_update(rwlock_ptr, this_pc, LSTAT_ACT_NO_WAIT); + } else { + lstat_update(rwlock_ptr, this_pc, LSTAT_ACT_REJECT); + } + + return retval; +} + +static void +init_control_space(void) +{ + /* Set all control space pointers to null and indices to "empty" */ + int cpu; + + lstat_control.hashtab = NULL; + lstat_control.dir = NULL; + for (cpu=0; cpu max_len) + return actual_ret_bcount; + + copy_to_user(buffer, (void *)&req, next_ret_bcount); + actual_ret_bcount += next_ret_bcount; + + if (!lstat_control.counts[0]) /* not initialized? */ + return actual_ret_bcount; + + next_ret_bcount = sizeof(lstat_cpu_counts_t); + for (cpu = 0; cpu < smp_num_cpus; cpu++) { + if ((actual_ret_bcount + next_ret_bcount) > max_len) + return actual_ret_bcount; /* leave early */ + copy_to_user(buffer + actual_ret_bcount, lstat_control.counts[cpu], + next_ret_bcount); + actual_ret_bcount += next_ret_bcount; + } + + next_ret_bcount = LSTAT_MAX_STAT_INDEX * sizeof(lstat_directory_entry_t); + if ( ((actual_ret_bcount + next_ret_bcount) > max_len) + || !lstat_control.dir ) + return actual_ret_bcount; /* leave early */ + + copy_to_user(buffer + actual_ret_bcount, lstat_control.dir, + next_ret_bcount); + actual_ret_bcount += next_ret_bcount; + + return actual_ret_bcount; +} + +/* + * Writing to the /proc lockmeter node enables or disables metering. + * based upon the first byte of the "written" data. + * To reset: disable, then re-enable. + */ +ssize_t put_lockmeter_info(const char *buffer, size_t len) +{ + int error = 0; + int dirsize, countsize, hashsize; + int cpu; + char put_char; + + if (len <= 0) + return -EINVAL; + + nonmetered_spin_lock(&lstat_control.control_lock); + + get_user(put_char, buffer); + switch (put_char) { + + case LSTAT_OFF: + if (lstat_control.state != LSTAT_OFF) { + lstat_control.state = LSTAT_OFF; + /* don't bother to deallocate the structures */ + } else { + error = -EBUSY; /* already OFF */ + } + break; + + case LSTAT_ON: + if (lstat_control.state == LSTAT_OFF) { + spin_lock_init(&lstat_control.directory_lock); + lstat_control.next_free_dir_index = 1; /* 0 is for overflows */ + + dirsize = LSTAT_MAX_STAT_INDEX * sizeof(lstat_directory_entry_t); + hashsize = (1 + LSTAT_HASH_TABLE_SIZE) * sizeof(ushort); + countsize = sizeof(lstat_cpu_counts_t); + + if (!lstat_control.hashtab) { + init_control_space(); /* guarantee all pointers at zero */ + + lstat_control.hashtab = kmalloc(hashsize, GFP_KERNEL); + if (!lstat_control.hashtab) + error = -ENOSPC; + + lstat_control.dir = kmalloc(dirsize, GFP_KERNEL); + if (!lstat_control.dir) + error = -ENOSPC; + + for (cpu = 0; cpu +#include +#include +#include +#include + +#include +#include + +/* + * Design: + * + * Every 32 bytes block of memory in the system has an 'allocation map entry'. + * A map entry is a two-fields bitmap, an 'ID' pointing to the allocator, + * and a ~2 seconds granularity timestamp to see object age. + * ID 0 means the block is empty. The allocation map is a very big static + * array, and is preallocated at boot, and put at the end of memory. + * + * This method relies on the fact that no object allocated in Linux + * is smaller than 32 bytes. True that we waste ~10% memory, but the + * method is very simple, extremely fast and generic. There are lots of + * systems that can tolerate 10% less memory, but almost no system + * can tolerate the CPU load caused by O(N) or O(log(N)) 'bookeeping + * algorithms' when allocating memory in a RL system. + * + * This method is a O(1) algorithm. + * + * + * Currently wrapped allocators: + * + * generic page allocator: get_free_pages()/free_pages() + * kernel allocator: kmalloc()/kfree()/kfree_s() + * kmem_cache_create()/kmem_cache_shrink() + * kmem_cache_alloc()/kmem_cache_free() + * networking allocator: skb_alloc()/skb_free() + * + * vmalloc()/vfree() will probably never be supported by + * this method, maybe we can represent them through their + * first physical page. It's not a common allocation + * method. + */ + +#define MIN_MEMBLOCK_SIZE 32 + +#define IDX(addr) (__pa(addr)/MIN_MEMBLOCK_SIZE) + +/* + * We want to keep the allocation map as small as possible + */ +#define ID_BITS 10 +#define MAX_ID (1<", 0 }; +static unsigned int curr_id = 0; +spinlock_t memleak_alloc_lock = SPIN_LOCK_UNLOCKED; +int memleak_errcount = 0; + +int alloc_addr_lock(unsigned long addr, struct alloc_struct * id) +{ + unsigned long flags, idx; + + if(!curr_id || !id) /* don't do anything if turned off */ + return 0; + + idx = IDX(addr); + + if (idx > ENTRIES) { + PROBLEM(); + return -1; + } + + spin_lock_irqsave(&memleak_alloc_lock, flags); + + /* If someone else registered ID while I was aquiring, take shortcut + * and only make the alloc_map entry. + */ + if(id->id) + goto alloc_ok; + else + { +#if 0 + printk("allocating ID.%d for %s:%d.\n",curr_id, id->file, id->line); +#endif + id->id = curr_id; + curr_id++; + if (curr_id == MAX_ID) { + printk("ID wrapped around, stopping ID allocation.\n"); + printk("Increase ID_BITS in memleak.c.\n"); + curr_id = 0; + } else + id_map[curr_id-1] = id; + } +alloc_ok: + alloc_map[idx].id = id->id; + alloc_map[idx].timestamp = jiffies>>ID_BITS; + spin_unlock_irqrestore(&memleak_alloc_lock, flags); + return 0; +} + +int alloc_addr_nolock(unsigned long addr, struct alloc_struct * id) +{ + unsigned long idx; + + if(!curr_id || !id ) /* don't do anything if turned off */ + return 0; + + idx = IDX(addr); + + if (idx > ENTRIES) { + PROBLEM(); + return -1; + } + + /* If someone else has already registered ID, take shortcut + * and only make the alloc_map entry. + */ + if(id->id) + goto alloc_ok; +#if 0 + printk("allocating ID.%d for %s:%d.\n",curr_id, id->file, id->line); +#endif + id->id = curr_id; + curr_id++; + if (curr_id == MAX_ID) { + printk("ID wrapped around, stopping ID allocation.\n"); + printk("Increase ID_BITS in memleak.c.\n"); + curr_id = 0; + } else + id_map[curr_id-1] = id; +alloc_ok: + alloc_map[idx].id = id->id; + alloc_map[idx].timestamp = jiffies>>ID_BITS; + return 0; +} + +int free_addr(unsigned long addr) +{ + unsigned idx; + + if(!curr_id) + return 0; + + idx = IDX(addr); + + if (idx > ENTRIES) { + PROBLEM(); + return -1; + } + + alloc_map[idx].id = 0; + return 0; +} + +/* + * We put the alloc table at the end of physical memory + */ +void memleak_init () +{ + unsigned long MEMSIZE, size, goal; + + id_map[0] = &NULL_id; + + MEMSIZE = max_low_pfn << PAGE_SHIFT; + ENTRIES = MEMSIZE/(MIN_MEMBLOCK_SIZE+sizeof(struct alloc_entry))+1; + size = ENTRIES * sizeof(struct alloc_entry); + + printk("MEMLEAK, allocating %ld KB allocation map.\n", size/1024); + + /* we must be called immediately after paging_init() so that we can + * steal the top of physical memory before anyone else does. + */ + + goal = (MEMSIZE - size) & PAGE_MASK; + alloc_map = (struct alloc_entry *) __alloc_bootmem(size, PAGE_SIZE, goal); + + if (!alloc_map) { + PROBLEM(); + for(;;); + } + + if (alloc_map != __va(goal)) + printk("MEMLEAK, Ouch!.. &alloc_map:0x%p requested:0x%p\n", alloc_map, __va(goal)); + + memset(alloc_map,0,size); +} + +void memleak_start (void) +{ + curr_id = 1; + memleak_errcount = 0; +} + +int memleak_read_proc(char *page, char **start, off_t idx, + int count, int *eof, void *data) +{ + struct alloc_struct * id; + unsigned long timestamp; + int n; + + repeat: + if (idx >= ENTRIES) + return 0; + + if (alloc_map[idx].id) { + id = id_map[alloc_map[idx].id]; + timestamp = (alloc_map[idx].timestamp< %s:%d (%ld)\n", + (void *)(idx*MIN_MEMBLOCK_SIZE),id->file,id->line,timestamp); + } else { + if (!idx) { + n = sprintf(page,"<%p> jiffies.c:%d (%ld)\n", NULL, 0, jiffies/HZ); + } + else + { + (*start)++; + idx++; + /* + * there is at least one allocation in the system + */ + goto repeat; + } + } + + (*start)++; + + return n; +} diff -urN 2.4.0/kernel/debug/profiler.c 2.4.0-ikd-1/kernel/debug/profiler.c --- 2.4.0/kernel/debug/profiler.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kernel/debug/profiler.c Mon Jan 15 18:40:05 2001 @@ -0,0 +1,421 @@ +/* + * linux/kernel/profiler.c + * + * Copyright (C) 1997 Ingo Molnar, Richard Henderson + * Copyright (C) 1998 Andrea Arcangeli + * + * This source is covered by the GNU GPL, the same as all kernel sources. + */ + +/* + * 'profiler.c' implements various profiling hacks, by abusing the profiling + * hook 'mcount', generated by GCC -pg + * + * Currently used for: + * + * - monitoring kernel stack usage and generating oopses when stack overflow + * - detecting software lockups + * - tracing the kernel + * + * Has to be a separate C module, because we have to compile it without -pg, + * to avoid recursion. + */ + +/* + * - print-eip is now a config option and it' s improved to give as the + * the execution order of the box and fixed some glitches. + * - developed CONFIG_PROFILE_GCC + * - developed CONFIG_KSTACK_METER + * - fixed get_stack_left() to handle the 8k 2.1.x kernel stack size. + * -arca + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Generally we dislike #ifdef's in main modules, but these mcount() based + * features are is too performance-sensitive to make them an all or nothing + * option, and too small to be put into header files. + */ + +#ifdef CONFIG_DEBUG_MCOUNT /* any mcount() functions activated? */ + +#ifdef CONFIG_TRACE + +spinlock_t trace_table_lock = SPIN_LOCK_UNLOCKED; +struct trace_table *trace_table = NULL; + +#endif /* CONFIG_TRACE */ + +#ifdef CONFIG_KSTACK_METER +struct { + unsigned int min_left_stack; + profiler_pc_t stack_eater_eip; +} kstack_meter = {-1UL, 0,}; + +static spinlock_t stack_meter_lock = SPIN_LOCK_UNLOCKED; +#endif + +/* deal with too early calls to mcount() and recursion */ +atomic_t mcount_ready = ATOMIC_INIT(0); +int sysctl_disable_mcount = 0; +#ifdef CONFIG_TRACE +atomic_t mcount_trace_ready = ATOMIC_INIT(0); +#endif + +void mcount_init (void) +{ +#ifdef CONFIG_TRACE + if ((trace_table = (struct trace_table *) alloc_bootmem(sizeof(*trace_table))) == NULL) { + printk("mcount_init: cannot allocate trace_table, size %lu. No tracing possible.\n", (unsigned long) sizeof(*trace_table)); + } + else { + trace_table->table_size = CONFIG_TRACE_SIZE; + trace_table->curr_call = 0; + memset(trace_table->entries, 0, sizeof(trace_table->entries)); + spin_lock_init(&trace_table_lock); +#ifdef CONFIG_TRACE_TIMESTAMP +#ifdef __i386__ + if (!(test_bit(X86_FEATURE_TSC, boot_cpu_data.x86_capability))) + printk("mcount_init: cpu does not support rdtsc, timestamps are jiffies instead\n"); +#else + printk("mcount_init: not i386 cpu, timestamps are jiffies instead\n"); +#endif /* __i386__ */ +#endif /* CONFIG_TRACE_TIMESTAMP */ + RESUME_MCOUNT_TRACE; /* start it */ + } +#endif /* CONFIG_TRACE */ + + printk("mcount_init\n"); + /* + * Ok, from now on it's for real: + */ + RESUME_MCOUNT; /* start it */ +} + +#ifdef CONFIG_TRACE + +/* Strictly speaking this routine should get the trace_table spin lock. + * However it is rarely used and may not be in a safe context to get the + * lock so we just dump the table and hope it does not change under us. + */ + +void print_emergency_trace (void) +{ + struct trace_entry *t; + int i, j; + + SUSPEND_MCOUNT_TRACE; + printk ("[] "); + +/* + * Well, 30 entries is pretty arbitrary, seems to be a reasonable value. + */ + j = trace_table->curr_call-CONFIG_ETRACE_LENGTH; + for (i=0; ientries[j++]); + if(!(i % 4)) + printk("\n"); + /* ksymoops expects [] */ + printk ("[<%08lx>] ", t->pc); +#ifdef CONFIG_TRACE_PID + printk("(%d) ", t->pid); +#endif +#if defined(CONFIG_TRACE_CPU) && defined(CONFIG_SMP) + printk("(%d) ", t->cpu); +#endif + } + RESUME_MCOUNT_TRACE; +} +#endif /* CONFIG_TRACE */ + +/* + * this (64 bytes) is twice as big as cachelines, but we cannot + * guarantee cacheline alignment ... too bad. So we waste two + * cachelines in the bad case. + * + * cacheline alignment is absolutely vital in this case, as these + * variables are higher frequented than say .. "current", and they + * should stay local on the owner CPU under all circumstances. + */ +struct cacheline_t { unsigned int i; }; + +#ifdef CONFIG_PRINT_EIP +/* + * Use this as last resort, when nothing else helps. If a hard lockup + * happens then you can decode the last EIP from the binary coded + * form on the screen. + */ + +static __inline__ void print_eip(profiler_pc_t eip) +{ +#if defined(__i386__) || defined(__ia64__) +#define video ((short int *)(0xb8000 + PAGE_OFFSET)) +#endif +#define HISTORY 24 +#define ALIGN __cacheline_aligned + + int i, value; + unsigned int tmp; + + /* + * We split the codepath in a dumb way, to get speed and proper + * per-CPU execution. + */ +#ifdef CONFIG_SMP + if (!smp_processor_id()) + { +#endif + static struct cacheline_t curr_pos_0 ALIGN ={0,}; + static unsigned int count_0 = 0; + /* + * we cover 1M of code currently ... should be enuff + */ + if ((curr_pos_0.i += 80) == HISTORY*80) + curr_pos_0.i = 0; + + for (i=7; i>=0; i--) + { + /* + * mask off the hexa digits one by one. + */ + value = eip & 0xf; + if (value<10) + *(video+i+curr_pos_0.i) = 0x5400 + (value+'0'); + else + *(video+i+curr_pos_0.i) = 0x5400 + (value-10+'a'); + eip >>= 4; + } + /* *(video+8+curr_pos_0.i) = 0x5400 + '=';*/ + tmp = count_0++; + for (i=3; i>=0; i--) + { + /* + * mask off the hexa digits one by one. + */ + value = tmp & 0xf; + if (value<10) + *(video+i+9+curr_pos_0.i) = 0x5400 + (value+'0'); + else + *(video+i+9+curr_pos_0.i) = 0x5400 + (value-10+'a'); + tmp >>= 4; + } +#ifdef CONFIG_SMP + } else { + static struct cacheline_t curr_pos_1 ALIGN ={0,}; + static unsigned int count_1 = 0; + /* + * we cover 1M of code currently ... should be enuff + */ + + if ((curr_pos_1.i += 80) == HISTORY*80) + curr_pos_1.i = 0; + + for (i=7; i>=0; i--) { + /* + * mask off the hexa digits one by one. + */ + value = eip & 0xf; + if (value<10) + *(video+40+i+curr_pos_1.i) = 0x6400 + (value+'0'); + else + *(video+40+i+curr_pos_1.i) = 0x6400 + (value-10+'a'); + eip >>= 4; + } + /* *(video+48+curr_pos_1.i) = 0x6400 + '=';*/ + tmp = count_1++; + for (i=3; i>=0; i--) { + /* + * mask off the hexa digits one by one. + */ + value = tmp & 0xf; + if (value<10) + *(video+i+49+curr_pos_1.i) = 0x6400 + (value+'0'); + else + *(video+i+49+curr_pos_1.i) = 0x6400 + (value-10+'a'); + tmp >>= 4; + } + } +#endif /* CONFIG_SMP */ + +#undef ALIGN +#undef HISTORY +#undef video +} + +#endif /* CONFIG_PRINT_EIP */ + +#ifdef CONFIG_PROFILE_GCC /* arca */ +static __inline__ void kernel_profiling(profiler_pc_t eip) +{ + extern char _stext; + extern unsigned int * prof_buffer; + + if (!prof_buffer) + return; + + eip -= (unsigned long) &_stext; + eip >>= prof_shift; + /* + * Don't ignore out-of-bounds EIP values silently, + * put them into the last histogram slot, so if + * present, they will show up as a sharp peak. + */ + if (eip > prof_len-1) + eip = prof_len-1; + + atomic_inc((atomic_t *)&prof_buffer[eip]); +} +#endif + +/* Watch this routine and mcount for any hidden calls to external + * routines. On SMP, something as simple as save_flags() calls + * __global_save_flags() in irq.c. If that module was compiled with + * -pg it calls back to mcount, stack overflow due to recursion. nm + * profiler.o should show no references to external procedures except + * for printk and vmalloc (from mcount_init). KAO. + */ + +inline int mcount_internal(profiler_pc_t self_addr) +{ +#ifdef CONFIG_PRINT_EIP + print_eip(self_addr); +#endif + +#ifdef CONFIG_PROFILE_GCC + kernel_profiling(self_addr); +#endif + +#ifdef CONFIG_DEBUG_SOFTLOCKUP + switch (current->deadlock_count) { + case 0: + if (current->pid) { + SUSPEND_MCOUNT; + printk("Deadlock threshold zero, should not happen, pid %d\n", current->pid); + RESUME_MCOUNT; + } + current->deadlock_count--; + return 0; + + case 1: + /* + * Oops on return. Do the oops outside this routine so + * mcount_ready and trace_table_lock are in a clean state. + */ + current->deadlock_count = 0; + /* no more mcount() processing for this process */ + SUSPEND_MCOUNT_PROC(current); + printk("Deadlock threshold exceeded, forcing Oops.\n"); + return 1; /* caller should oops */ + break; + + default: + current->deadlock_count--; + break; + } +#endif /* CONFIG_DEBUG_SOFTLOCKUP */ + +#ifdef CONFIG_DEBUG_KSTACK + if (get_stack_left() < CONFIG_KSTACK_THRESHOLD) { + SUSPEND_MCOUNT_PROC(current); + printk(KERN_ALERT "kernel stack overflow. Forcing Oops.\n"); + return 1; + } +#endif /* CONFIG_DEBUG_KSTACK */ + +#ifdef CONFIG_KSTACK_METER /* arca */ + { + unsigned int left_stack, flags; + + /* + * One CPU per time to be sure that min_left_stack is really + * the minimum. -arca + */ + spin_lock_irqsave(&stack_meter_lock, flags); + left_stack = get_stack_left() - sizeof(struct task_struct); + if (left_stack < kstack_meter.min_left_stack) + { + kstack_meter.min_left_stack = left_stack; + kstack_meter.stack_eater_eip = self_addr; + } + spin_unlock_irqrestore(&stack_meter_lock, flags); + } +#endif + +#ifdef CONFIG_TRACE + { + /* Protected by trace_table_lock */ + struct trace_entry *t; + ++(trace_table->curr_call); + while (trace_table->curr_call >= CONFIG_TRACE_SIZE) { + trace_table->curr_call -= CONFIG_TRACE_SIZE; + } + + t = &(trace_table->entries[trace_table->curr_call]); + + t->pc = self_addr; +#ifdef CONFIG_TRACE_TIMESTAMP + t->timestamp = get_profiler_timestamp(); +#endif +#ifdef CONFIG_TRACE_PID + t->pid = current->pid; +#endif +#if defined(CONFIG_TRACE_CPU) && defined(CONFIG_SMP) + t->cpu = smp_processor_id(); +#endif + } +#endif /* CONFIG_TRACE */ + return 0; +} + +#ifdef __ia64__ +void mcount(profiler_pc_t previous_eip, profiler_pc_t eip) +#else +void mcount(void) +#endif +{ + int do_oops; +#ifndef __ia64__ + profiler_pc_t eip; +#endif +#ifdef CONFIG_TRACE + unsigned long flags; +#endif + if (sysctl_disable_mcount || atomic_read(&mcount_ready) <= 0) + return; + +#ifdef CONFIG_TRACE + if (atomic_read(&mcount_trace_ready) <= 0) + return; +#endif + + if (current->flags & PF_NO_MCOUNT) + return; + + +#ifndef __ia64__ + eip = (profiler_pc_t) __builtin_return_address(0); +#endif + + LOCK_MCOUNT_TRACE(flags); + do_oops = mcount_internal(eip); + UNLOCK_MCOUNT_TRACE(flags); + + /* Do oops with mcount_ready and trace_table_lock in a clean state */ + if (do_oops) + *(char *)0=0; +} + +#ifdef CONFIG_MODULES +EXPORT_SYMBOL_NOVERS(mcount); +#endif + +#endif /* CONFIG_DEBUG_MCOUNT */ diff -urN 2.4.0/kernel/fork.c 2.4.0-ikd-1/kernel/fork.c --- 2.4.0/kernel/fork.c Fri Jan 5 02:19:30 2001 +++ 2.4.0-ikd-1/kernel/fork.c Mon Jan 15 18:40:05 2001 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -594,9 +595,14 @@ p->did_exec = 0; p->swappable = 0; +#ifdef CONFIG_DEBUG_SOFTLOCKUP +#warning Do something about KSTACK here ... + p->deadlock_count=CONFIG_SOFTLOCKUP_THRESHOLD; +#endif p->state = TASK_UNINTERRUPTIBLE; copy_flags(clone_flags, p); + RESUME_MCOUNT_PROC(p); p->pid = get_pid(clone_flags); p->run_list.next = NULL; diff -urN 2.4.0/kernel/kallsyms.c 2.4.0-ikd-1/kernel/kallsyms.c --- 2.4.0/kernel/kallsyms.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/kernel/kallsyms.c Mon Jan 15 18:40:05 2001 @@ -0,0 +1,306 @@ +/* An example of using kallsyms data in a kernel debugger. + + Copyright 2000 Keith Owens April 2000 + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + 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. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ident "$Id$" + +/* + This code uses the list of all kernel and module symbols to :- + + * Find any non-stack symbol in a kernel or module. Symbols do + not have to be exported for debugging. + + * Convert an address to the module (or kernel) that owns it, the + section it is in and the nearest symbol. This finds all non-stack + symbols, not just exported ones. + + You need modutils >= 2.3.11 and a kernel with the kallsyms patch + which was compiled with CONFIG_KALLSYMS. + */ + +#include +#include +#include +#include +#include + +/* These external symbols are only set on kernels compiled with + * CONFIG_KALLSYMS. + */ + +extern const char __start___kallsyms[]; +extern const char __stop___kallsyms[]; + +static struct module **kallsyms_module_list; + +static void kallsyms_get_module_list(void) +{ + const struct kallsyms_header *ka_hdr; + const struct kallsyms_section *ka_sec; + const struct kallsyms_symbol *ka_sym; + const char *ka_str; + int i; + const char *p; + + if (__start___kallsyms >= __stop___kallsyms) + return; + ka_hdr = (struct kallsyms_header *)__start___kallsyms; + ka_sec = (struct kallsyms_section *) + ((char *)(ka_hdr) + ka_hdr->section_off); + ka_sym = (struct kallsyms_symbol *) + ((char *)(ka_hdr) + ka_hdr->symbol_off); + ka_str = + ((char *)(ka_hdr) + ka_hdr->string_off); + + for (i = 0; i < ka_hdr->symbols; kallsyms_next_sym(ka_hdr, ka_sym), ++i) { + p = ka_str + ka_sym->name_off; + if (strcmp(p, "module_list") == 0) { + if (ka_sym->symbol_addr) + kallsyms_module_list = (struct module **)(ka_sym->symbol_addr); + break; + } + } +} + +static void __inline__ kallsyms_do_first_time(void) +{ + static int first_time = 1; + if (first_time) + kallsyms_get_module_list(); + first_time = 0; +} + +/* A symbol can appear in more than one module. A token is used to + * restart the scan at the next module, set the token to 0 for the + * first scan of each symbol. + */ + +int kallsyms_symbol_to_address( + const char *name, /* Name to lookup */ + unsigned long *token, /* Which module to start at */ + const char **mod_name, /* Set to module name */ + unsigned long *mod_start, /* Set to start address of module */ + unsigned long *mod_end, /* Set to end address of module */ + const char **sec_name, /* Set to section name */ + unsigned long *sec_start, /* Set to start address of section */ + unsigned long *sec_end, /* Set to end address of section */ + const char **sym_name, /* Set to full symbol name */ + unsigned long *sym_start, /* Set to start address of symbol */ + unsigned long *sym_end /* Set to end address of symbol */ + ) +{ + const struct kallsyms_header *ka_hdr = NULL; /* stupid gcc */ + const struct kallsyms_section *ka_sec; + const struct kallsyms_symbol *ka_sym = NULL; + const char *ka_str = NULL; + const struct module *m; + int i = 0, l; + const char *p, *pt_R; + char *p2; + + kallsyms_do_first_time(); + if (!kallsyms_module_list) + return(0); + + /* Restart? */ + m = *kallsyms_module_list; + if (token && *token) { + for (; m; m = m->next) + if ((unsigned long)m == *token) + break; + if (m) + m = m->next; + } + + for (; m; m = m->next) { + if (!mod_member_present(m, kallsyms_start) || + !mod_member_present(m, kallsyms_end) || + m->kallsyms_start >= m->kallsyms_end) + continue; + ka_hdr = (struct kallsyms_header *)m->kallsyms_start; + ka_sym = (struct kallsyms_symbol *) + ((char *)(ka_hdr) + ka_hdr->symbol_off); + ka_str = + ((char *)(ka_hdr) + ka_hdr->string_off); + for (i = 0; i < ka_hdr->symbols; ++i, kallsyms_next_sym(ka_hdr, ka_sym)) { + p = ka_str + ka_sym->name_off; + if (strcmp(p, name) == 0) + break; + /* Unversioned requests match versioned names */ + if (!(pt_R = strstr(p, "_R"))) + continue; + l = strlen(pt_R); + if (l < 10) + continue; /* Not _R.*xxxxxxxx */ + (void)simple_strtoul(pt_R+l-8, &p2, 16); + if (*p2) + continue; /* Not _R.*xxxxxxxx */ + if (strncmp(p, name, pt_R-p) == 0) + break; /* Match with version */ + } + if (i < ka_hdr->symbols) + break; + } + + if (token) + *token = (unsigned long)m; + if (!m) + return(0); /* not found */ + + ka_sec = (const struct kallsyms_section *) + ((char *)ka_hdr + ka_hdr->section_off + ka_sym->section_off); + *mod_name = *(m->name) ? m->name : "kernel"; + *mod_start = ka_hdr->start; + *mod_end = ka_hdr->end; + *sec_name = ka_sec->name_off + ka_str; + *sec_start = ka_sec->start; + *sec_end = ka_sec->start + ka_sec->size; + *sym_name = ka_sym->name_off + ka_str; + *sym_start = ka_sym->symbol_addr; + if (i < ka_hdr->symbols-1) { + const struct kallsyms_symbol *ka_symn = ka_sym; + kallsyms_next_sym(ka_hdr, ka_symn); + *sym_end = ka_symn->symbol_addr; + } + else + *sym_end = *sec_end; + return(1); +} + +int kallsyms_address_to_symbol( + unsigned long address, /* Address to lookup */ + const char **mod_name, /* Set to module name */ + unsigned long *mod_start, /* Set to start address of module */ + unsigned long *mod_end, /* Set to end address of module */ + const char **sec_name, /* Set to section name */ + unsigned long *sec_start, /* Set to start address of section */ + unsigned long *sec_end, /* Set to end address of section */ + const char **sym_name, /* Set to full symbol name */ + unsigned long *sym_start, /* Set to start address of symbol */ + unsigned long *sym_end /* Set to end address of symbol */ + ) +{ + const struct kallsyms_header *ka_hdr = NULL; /* stupid gcc */ + const struct kallsyms_section *ka_sec = NULL; + const struct kallsyms_symbol *ka_sym; + const char *ka_str; + const struct module *m; + int i; + unsigned long end; + + kallsyms_do_first_time(); + if (!kallsyms_module_list) + return(0); + + for (m = *kallsyms_module_list; m; m = m->next) { + if (!mod_member_present(m, kallsyms_start) || + !mod_member_present(m, kallsyms_end) || + m->kallsyms_start >= m->kallsyms_end) + continue; + ka_hdr = (struct kallsyms_header *)m->kallsyms_start; + ka_sec = (const struct kallsyms_section *) + ((char *)ka_hdr + ka_hdr->section_off); + /* Is the address in any section in this module? */ + for (i = 0; i < ka_hdr->sections; ++i, kallsyms_next_sec(ka_hdr, ka_sec)) { + if (ka_sec->start <= address && + (ka_sec->start + ka_sec->size) > address) + break; + } + if (i < ka_hdr->sections) + break; /* Found a matching section */ + } + + if (!m) + return(0); /* not found */ + + ka_sym = (struct kallsyms_symbol *) + ((char *)(ka_hdr) + ka_hdr->symbol_off); + ka_str = + ((char *)(ka_hdr) + ka_hdr->string_off); + *mod_name = *(m->name) ? m->name : "kernel"; + *mod_start = ka_hdr->start; + *mod_end = ka_hdr->end; + *sec_name = ka_sec->name_off + ka_str; + *sec_start = ka_sec->start; + *sec_end = ka_sec->start + ka_sec->size; + *sym_name = *sec_name; /* In case we find no matching symbol */ + *sym_start = *sec_start; + *sym_end = *sec_end; + + for (i = 0; i < ka_hdr->symbols; ++i, kallsyms_next_sym(ka_hdr, ka_sym)) { + if (ka_sym->symbol_addr > address) + continue; + if (i < ka_hdr->symbols-1) { + const struct kallsyms_symbol *ka_symn = ka_sym; + kallsyms_next_sym(ka_hdr, ka_symn); + end = ka_symn->symbol_addr; + } + else + end = *sec_end; + if (end <= address) + continue; + if ((char *)ka_hdr + ka_hdr->section_off + ka_sym->section_off + != (char *)ka_sec) + continue; /* wrong section */ + *sym_name = ka_str + ka_sym->name_off; + *sym_start = ka_sym->symbol_addr; + *sym_end = end; + break; + } + return(1); +} + +/* List all sections in all modules. The callback routine is invoked with + * token, module name, section name, section start, section end, section flags. + */ +int kallsyms_sections(void *token, + int (*callback)(void *, const char *, const char *, ElfW(Addr), ElfW(Addr), ElfW(Word))) +{ + const struct kallsyms_header *ka_hdr = NULL; /* stupid gcc */ + const struct kallsyms_section *ka_sec = NULL; + const char *ka_str; + const struct module *m; + int i; + + kallsyms_do_first_time(); + if (!kallsyms_module_list) + return(0); + + for (m = *kallsyms_module_list; m; m = m->next) { + if (!mod_member_present(m, kallsyms_start) || + !mod_member_present(m, kallsyms_end) || + m->kallsyms_start >= m->kallsyms_end) + continue; + ka_hdr = (struct kallsyms_header *)m->kallsyms_start; + ka_sec = (const struct kallsyms_section *) ((char *)ka_hdr + ka_hdr->section_off); + ka_str = ((char *)(ka_hdr) + ka_hdr->string_off); + for (i = 0; i < ka_hdr->sections; ++i, kallsyms_next_sec(ka_hdr, ka_sec)) { + if (callback( + token, + *(m->name) ? m->name : "kernel", + ka_sec->name_off + ka_str, + ka_sec->start, + ka_sec->start + ka_sec->size, + ka_sec->flags)) + return(0); + } + } + return(1); +} diff -urN 2.4.0/kernel/ksyms.c 2.4.0-ikd-1/kernel/ksyms.c --- 2.4.0/kernel/ksyms.c Fri Jan 5 02:19:30 2001 +++ 2.4.0-ikd-1/kernel/ksyms.c Mon Jan 15 18:40:05 2001 @@ -52,6 +52,9 @@ #ifdef CONFIG_KMOD #include #endif +#ifdef CONFIG_KALLSYMS +#include +#endif extern void set_device_ro(kdev_t dev,int flag); @@ -77,6 +80,15 @@ EXPORT_SYMBOL(inter_module_put); EXPORT_SYMBOL(try_inc_mod_count); +#ifdef CONFIG_KALLSYMS +extern const char __start___kallsyms[]; +extern const char __stop___kallsyms[]; +EXPORT_SYMBOL(__start___kallsyms); +EXPORT_SYMBOL(__stop___kallsyms); +EXPORT_SYMBOL(kallsyms_symbol_to_address); +EXPORT_SYMBOL(kallsyms_address_to_symbol); +#endif + /* process memory management */ EXPORT_SYMBOL(do_mmap_pgoff); EXPORT_SYMBOL(do_munmap); @@ -87,10 +99,24 @@ EXPORT_SYMBOL(exit_sighand); /* internal kernel memory management */ +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(__alloc_pages); EXPORT_SYMBOL(alloc_pages_node); EXPORT_SYMBOL(__get_free_pages); EXPORT_SYMBOL(get_zeroed_page); +#else +EXPORT_SYMBOL(__alloc_pages_wrap); +EXPORT_SYMBOL(alloc_pages_node_wrap); +EXPORT_SYMBOL(__get_free_pages_wrap); +EXPORT_SYMBOL(get_zeroed_page_wrap); +EXPORT_SYMBOL(kmem_cache_create_wrap); +EXPORT_SYMBOL(kmem_cache_alloc_wrap); +EXPORT_SYMBOL(kmalloc_wrap); +EXPORT_SYMBOL(__vmalloc_wrap); +EXPORT_SYMBOL(alloc_addr_lock); +EXPORT_SYMBOL(alloc_addr_nolock); +EXPORT_SYMBOL(free_addr); +#endif /* CONFIG_MEMLEAK */ EXPORT_SYMBOL(__free_pages); EXPORT_SYMBOL(free_pages); #ifndef CONFIG_DISCONTIGMEM @@ -98,15 +124,23 @@ #endif EXPORT_SYMBOL(num_physpages); EXPORT_SYMBOL(kmem_find_general_cachep); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(kmem_cache_create); +#endif /* CONFIG_MEMLEAK */ EXPORT_SYMBOL(kmem_cache_destroy); EXPORT_SYMBOL(kmem_cache_shrink); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(kmem_cache_alloc); +#endif /* CONFIG_MEMLEAK */ EXPORT_SYMBOL(kmem_cache_free); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(kmalloc); +#endif /* CONFIG_MEMLEAK */ EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(vfree); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(__vmalloc); +#endif /* CONFIG_MEMLEAK */ EXPORT_SYMBOL(mem_map); EXPORT_SYMBOL(remap_page_range); EXPORT_SYMBOL(max_mapnr); @@ -536,3 +570,13 @@ EXPORT_SYMBOL(tasklist_lock); EXPORT_SYMBOL(pidhash); + + +#if defined(CONFIG_SMP) && defined(CONFIG_LOCKMETER) +EXPORT_SYMBOL(_spin_lock_); +EXPORT_SYMBOL(_spin_unlock_); +EXPORT_SYMBOL(_spin_trylock_); +EXPORT_SYMBOL(_read_lock_); +EXPORT_SYMBOL(_write_lock_); +EXPORT_SYMBOL(_write_trylock_); +#endif diff -urN 2.4.0/kernel/panic.c 2.4.0-ikd-1/kernel/panic.c --- 2.4.0/kernel/panic.c Thu Nov 16 15:37:43 2000 +++ 2.4.0-ikd-1/kernel/panic.c Mon Jan 15 18:40:05 2001 @@ -16,6 +16,9 @@ #include #include #include +#ifdef CONFIG_TRACE +#include +#endif asmlinkage void sys_sync(void); /* it's really int */ extern void unblank_console(void); @@ -51,6 +54,9 @@ unsigned long caller = (unsigned long) __builtin_return_address(0); #endif +#ifdef CONFIG_TRACE + SUSPEND_MCOUNT_TRACE; +#endif va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); diff -urN 2.4.0/kernel/printk.c 2.4.0-ikd-1/kernel/printk.c --- 2.4.0/kernel/printk.c Tue Jan 2 17:41:22 2001 +++ 2.4.0-ikd-1/kernel/printk.c Mon Jan 15 18:40:05 2001 @@ -12,6 +12,8 @@ * Modified for sysctl support, 1/8/97, Chris Horn. * Fixed SMP synchronization, 08/08/99, Manfred Spraul * manfreds@colorfullife.com + * syslog_to_console for SysRQ dumploGs. 12/04/1998. + * Keith Owens */ #include @@ -242,6 +244,45 @@ } out: return error; +} + +void syslog_to_console(void) +{ + /* + * Copy the syslog buffer to all registered consoles. Like + * sys_syslog, option 3 but to console instead of user. Raw data, + * no attempt to find record headers, message levels etc. + * Intended as a last ditch dump of syslog. + */ + unsigned long i, j, count, flags; + char *p, buf[129]; /* copy log in 128 byte chunks */ + + /* + * The logged_chars, log_start, and log_size values may + * change from an interrupt, so we disable interrupts. + */ + count = LOG_BUF_LEN; + spin_lock_irqsave(&console_lock, flags); + if (count > logged_chars) + count = logged_chars; + j = log_start + log_size - count; + spin_unlock_irqrestore(&console_lock, flags); + /* Race here, the log can change under us, we might dump garbage. + * Live with it, this is a last ditch output, waiting for locks + * could stop output. console_print should not require locks. + */ + for (i = 0, p = buf; i < count; i++) { + *p++ = *((char *) log_buf+(j++ & (LOG_BUF_LEN-1))); + if (p == buf+sizeof(buf)-1) { + *p = '\0'; + console_print(buf); + p = buf; + } + } + if (p != buf) { + *p = '\0'; + console_print(buf); + } } asmlinkage long sys_syslog(int type, char * buf, int len) diff -urN 2.4.0/kernel/sched.c 2.4.0-ikd-1/kernel/sched.c --- 2.4.0/kernel/sched.c Fri Jan 5 02:19:30 2001 +++ 2.4.0-ikd-1/kernel/sched.c Mon Jan 15 18:40:05 2001 @@ -641,6 +641,9 @@ } } +#ifdef CONFIG_DEBUG_SOFTLOCKUP + prev->deadlock_count=CONFIG_SOFTLOCKUP_THRESHOLD; +#endif /* * This just switches the register state and the * stack. diff -urN 2.4.0/kernel/signal.c 2.4.0-ikd-1/kernel/signal.c --- 2.4.0/kernel/signal.c Fri Jan 5 02:19:30 2001 +++ 2.4.0-ikd-1/kernel/signal.c Mon Jan 15 18:40:05 2001 @@ -13,6 +13,7 @@ #include #include #include +#include #include diff -urN 2.4.0/kernel/softirq.c 2.4.0-ikd-1/kernel/softirq.c --- 2.4.0/kernel/softirq.c Tue Jan 2 17:41:22 2001 +++ 2.4.0-ikd-1/kernel/softirq.c Mon Jan 15 18:40:05 2001 @@ -16,6 +16,7 @@ #include #include #include +#include /* - No shared variables, all the data are CPU local. @@ -258,12 +259,18 @@ hardirq_endlock(cpu); spin_unlock(&global_bh_lock); +#if defined(CONFIG_DEBUG_SOFTLOCKUP) && defined(CONFIG_SMP) + mcount(); +#endif return; resched_unlock: spin_unlock(&global_bh_lock); resched: mark_bh(nr); +#if defined(CONFIG_DEBUG_SOFTLOCKUP) && defined(CONFIG_SMP) + mcount(); +#endif } void init_bh(int nr, void (*routine)(void)) diff -urN 2.4.0/kernel/sys.c 2.4.0-ikd-1/kernel/sys.c --- 2.4.0/kernel/sys.c Thu Nov 16 15:37:43 2000 +++ 2.4.0-ikd-1/kernel/sys.c Mon Jan 15 18:40:05 2001 @@ -257,6 +257,37 @@ return retval; } +/* routines to trip various softlockup conditions, driven from reboot */ +static void kstack_test1 (void); +static void kstack_test2 (void); +static void kstack_test3 (void); +static void kstack_test4 (void); + +static void kstack_test1 (void) +{ + kstack_test2(); +} + +static void kstack_test2 (void) +{ + kstack_test3(); +} + +static void kstack_test3 (void) +{ + kstack_test4(); +} + +static void kstack_test4 (void) +{ + kstack_test1(); /* curse and recurse, stack overflow */ +} + +static volatile int softlockup_count=0; +void softlockup_looptest(void) +{ + softlockup_count++; +} /* * Reboot system call: for obvious reasons only root may call it, @@ -320,6 +351,34 @@ notifier_call_chain(&reboot_notifier_list, SYS_RESTART, buffer); printk(KERN_EMERG "Restarting system with command '%s'.\n", buffer); machine_restart(buffer); + break; + + case LINUX_REBOOT_CMD_OOPS: + /* Kernel oops, the machine should recover afterwards */ + *(char *)0=0; + break; + + /* Trip various software lockup conditions. Overloading sys_reboot + * because they do not justify their own syscall. These do not notify + * the reboot list. + */ + + case LINUX_REBOOT_CMD_STACKFAULT: + /* stack fault via endless recursion */ +#ifndef CONFIG_DEBUG_KSTACK + printk(KERN_WARNING "Invoking STACKFAULT without CONFIG_DEBUG_KSTACK\n" + "Machine may not recover!\n"); +#endif + kstack_test1(); + break; + + case LINUX_REBOOT_CMD_KERNEL_LOOP: + /* lockup via endless loop */ +#ifndef CONFIG_DEBUG_SOFTLOCKUP + printk(KERN_WARNING "Invoking KERNEL_LOOP without CONFIG_DEBUG_SOFTLOCKUP\n" + "Machine may not recover!\n"); +#endif + for (;;) softlockup_looptest(); break; default: diff -urN 2.4.0/kernel/sysctl.c 2.4.0-ikd-1/kernel/sysctl.c --- 2.4.0/kernel/sysctl.c Tue Jan 2 17:41:22 2001 +++ 2.4.0-ikd-1/kernel/sysctl.c Mon Jan 15 18:40:05 2001 @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -133,6 +134,11 @@ extern int inodes_stat[]; extern int dentry_stat[]; +#ifdef CONFIG_X86_LOCAL_APIC +#include +static int do_proc_set_nmi_watchdog(ctl_table *, int, struct file *, void *, size_t *); +#endif + /* The default sysctl tables: */ static ctl_table root_table[] = { @@ -236,6 +242,14 @@ {KERN_OVERFLOWGID, "overflowgid", &overflowgid, sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL, &minolduid, &maxolduid}, +#ifdef CONFIG_X86_LOCAL_APIC + {KERN_NMI_WATCHDOG, "nmi_watchdog", &proc_nmi_watchdog, sizeof(int), + 0644, NULL, &do_proc_set_nmi_watchdog}, +#endif +#ifdef CONFIG_KDB + {KERN_KDB, "kdb", &kdb_on, sizeof(int), + 0644, NULL, &proc_dointvec}, +#endif /* CONFIG_KDB */ {0} }; @@ -298,7 +312,22 @@ {0} }; +#ifdef CONFIG_DEBUG_MCOUNT +extern int sysctl_disable_mcount; +#ifdef CONFIG_KSTACK_METER +extern int kstack_meter[]; +#endif +#endif + static ctl_table debug_table[] = { +#ifdef CONFIG_DEBUG_MCOUNT + {DEBUG_DISABLE_MCOUNT, "disable_mcount", &sysctl_disable_mcount, + sizeof(int), 0644, NULL, &proc_dointvec}, +#ifdef CONFIG_KSTACK_METER + {DEBUG_KSTACK_METER, "kstack_meter", &kstack_meter, 2*sizeof(int), + 0644, NULL, &proc_dointvec}, +#endif +#endif {0} }; @@ -815,6 +844,24 @@ { return do_proc_dointvec(table,write,filp,buffer,lenp,1,OP_SET); } + +#ifdef CONFIG_X86_LOCAL_APIC +/* nmi_watchdog must be processed here, no event will detect that it has + * been turned on. set_nmi_watchdog() can reject the change, /proc sees + * an external copy of nmi_watchdog. + */ +static int do_proc_set_nmi_watchdog(ctl_table *table, + int write, struct file *filp, + void *buffer, size_t *lenp) +{ + int ret; + if ((ret = proc_dointvec(table,write,filp,buffer,lenp))) + return(ret); + if (write) + ret = set_nmi_watchdog(proc_nmi_watchdog); + return ret; +} +#endif /* CONFIG_X86_LOCAL_APIC */ /* * init may raise the set. diff -urN 2.4.0/lib/string.c 2.4.0-ikd-1/lib/string.c --- 2.4.0/lib/string.c Thu Aug 10 22:10:14 2000 +++ 2.4.0-ikd-1/lib/string.c Mon Jan 15 18:40:05 2001 @@ -18,6 +18,7 @@ #include #include #include +#include #ifndef __HAVE_ARCH_STRNICMP int strnicmp(const char *s1, const char *s2, size_t len) @@ -376,3 +377,11 @@ } #endif + +char *kstrdup(const char *str, int type) +{ + int n = strlen(str)+1; + char *s = kmalloc(n, type); + if (!s) return NULL; + return strcpy(s, str); +} diff -urN 2.4.0/mm/bootmem.c 2.4.0-ikd-1/mm/bootmem.c --- 2.4.0/mm/bootmem.c Thu Nov 16 15:37:43 2000 +++ 2.4.0-ikd-1/mm/bootmem.c Mon Jan 15 18:40:05 2001 @@ -257,6 +257,10 @@ total += count; bdata->node_bootmem_map = NULL; +#ifdef CONFIG_MEMLEAK + { extern void memleak_start(void); memleak_start(); } +#endif + return total; } diff -urN 2.4.0/mm/numa.c 2.4.0-ikd-1/mm/numa.c --- 2.4.0/mm/numa.c Thu Nov 16 15:37:43 2000 +++ 2.4.0-ikd-1/mm/numa.c Mon Jan 15 18:40:05 2001 @@ -1,6 +1,10 @@ /* * Written by Kanoj Sarcar, SGI, Aug 1999 */ +#define MEMLEAK_PASS_ALLOCATION +#define MEMLEAK_UNWRAP_PAGE +#define MEMLEAK_UNWRAP_NUMA + #include #include #include @@ -8,6 +12,7 @@ #include #include #include +#include int numnodes = 1; /* Initialized for UMA platforms */ @@ -31,7 +36,11 @@ #endif /* !CONFIG_DISCONTIGMEM */ +#ifndef CONFIG_MEMLEAK struct page * alloc_pages_node(int nid, int gfp_mask, unsigned long order) +#else +struct page * alloc_pages_node_wrap(int nid, int gfp_mask, unsigned long order, struct alloc_struct *IDPTR) +#endif { #ifdef CONFIG_NUMA return __alloc_pages(NODE_DATA(nid)->node_zonelists + gfp_mask, order); @@ -92,7 +101,11 @@ * This can be refined. Currently, tries to do round robin, instead * should do concentratic circle search, starting from current node. */ +#ifndef CONFIG_MEMLEAK struct page * alloc_pages(int gfp_mask, unsigned long order) +#else +struct page * alloc_pages_wrap(int gfp_mask, unsigned long order, struct alloc_struct *IDPTR) +#endif { struct page *ret = 0; pg_data_t *start, *temp; diff -urN 2.4.0/mm/page_alloc.c 2.4.0-ikd-1/mm/page_alloc.c --- 2.4.0/mm/page_alloc.c Fri Jan 5 02:19:30 2001 +++ 2.4.0-ikd-1/mm/page_alloc.c Mon Jan 15 18:40:05 2001 @@ -9,6 +9,9 @@ * Zone balancing, Kanoj Sarcar, SGI, Jan 2000 */ +#define MEMLEAK_PASS_ALLOCATION +#define MEMLEAK_UNWRAP_PAGE + #include #include #include @@ -16,6 +19,7 @@ #include #include #include +#include int nr_swap_pages; int nr_active_pages; @@ -27,6 +31,18 @@ static int zone_balance_min[MAX_NR_ZONES] = { 10 , 10, 10, }; static int zone_balance_max[MAX_NR_ZONES] = { 255 , 255, 255, }; +#ifdef CONFIG_GFP_POISON +static unsigned long poison(unsigned long addr, unsigned long order) +{ + memset((char *) addr, 0x6b, PAGE_SIZE<list, &area->free_list); + POISON(page_address(page), order); + MEMLEAK_FREE(page_address(page)); spin_unlock_irqrestore(&zone->lock, flags); @@ -168,8 +186,12 @@ return page; } +#ifndef CONFIG_MEMLEAK static FASTCALL(struct page * rmqueue(zone_t *zone, unsigned long order)); static struct page * rmqueue(zone_t *zone, unsigned long order) +#else +static struct page * rmqueue_wrap (zone_t *zone, unsigned long order, struct alloc_struct *IDPTR) +#endif { free_area_t * area = zone->free_area + order; unsigned long curr_order = order; @@ -219,8 +241,13 @@ * and is separated out to keep the code size smaller. * (suggested by Davem at 1:30 AM, typed by Rik at 6 AM) */ +#ifndef CONFIG_MEMLEAK static struct page * __alloc_pages_limit(zonelist_t *zonelist, unsigned long order, int limit, int direct_reclaim) +#else +static struct page * __alloc_pages_limit_wrap(zonelist_t *zonelist, + unsigned long order, int limit, int direct_reclaim, struct alloc_struct *IDPTR) +#endif { zone_t **zone = zonelist->zones; @@ -270,7 +297,11 @@ /* * This is the 'heart' of the zoned buddy allocator: */ +#ifndef CONFIG_MEMLEAK struct page * __alloc_pages(zonelist_t *zonelist, unsigned long order) +#else +struct page * __alloc_pages_wrap(zonelist_t *zonelist, unsigned long order, struct alloc_struct *IDPTR) +#endif { zone_t **zone; int direct_reclaim = 0; @@ -523,7 +554,11 @@ /* * Common helper functions. */ +#ifndef CONFIG_MEMLEAK unsigned long __get_free_pages(int gfp_mask, unsigned long order) +#else +unsigned long __get_free_pages_wrap(int gfp_mask, unsigned long order, struct alloc_struct *IDPTR) +#endif { struct page * page; @@ -533,7 +568,11 @@ return (unsigned long) page_address(page); } +#ifndef CONFIG_MEMLEAK unsigned long get_zeroed_page(int gfp_mask) +#else +unsigned long get_zeroed_page_wrap(int gfp_mask, struct alloc_struct *IDPTR) +#endif { struct page * page; diff -urN 2.4.0/mm/slab.c 2.4.0-ikd-1/mm/slab.c --- 2.4.0/mm/slab.c Thu Nov 16 15:37:33 2000 +++ 2.4.0-ikd-1/mm/slab.c Mon Jan 15 18:40:05 2001 @@ -68,10 +68,19 @@ * */ +#define MEMLEAK_PASS_ALLOCATION +#define MEMLEAK_UNWRAP_SLAB + #include #include #include #include +#include +#include +#include + +#include +#include #include /* @@ -85,7 +94,11 @@ * FORCED_DEBUG - 1 enables SLAB_RED_ZONE and SLAB_POISON (if possible) */ +#ifndef CONFIG_SLAB_POISON #define DEBUG 0 +#else +#define DEBUG 1 +#endif #define STATS 0 #define FORCED_DEBUG 0 @@ -472,7 +485,12 @@ /* Interface to system's page allocator. No need to hold the cache-lock. */ -static inline void * kmem_getpages (kmem_cache_t *cachep, unsigned long flags) +#ifndef CONFIG_MEMLEAK +static inline void * kmem_getpages ( kmem_cache_t *cachep, unsigned long flags) +#else +static inline void * kmem_getpages_wrap (kmem_cache_t *cachep, + unsigned long flags, struct alloc_struct *IDPTR) +#endif { void *addr; @@ -607,10 +625,18 @@ * cacheline. This can be beneficial if you're counting cycles as closely * as davem. */ +#ifndef CONFIG_MEMLEAK kmem_cache_t * kmem_cache_create (const char *name, size_t size, size_t offset, unsigned long flags, void (*ctor)(void*, kmem_cache_t *, unsigned long), void (*dtor)(void*, kmem_cache_t *, unsigned long)) +#else +kmem_cache_t * +kmem_cache_create_wrap (const char *name, size_t size, size_t offset, + unsigned long flags, void (*ctor)(void*, kmem_cache_t *, unsigned long), + void (*dtor)(void*, kmem_cache_t *, unsigned long), + struct alloc_struct *IDPTR) +#endif { const char *func_nm = KERN_ERR "kmem_create: "; size_t left_over, align, slab_size; @@ -990,12 +1016,19 @@ #endif kmem_cache_free(&cache_cache, cachep); + MEMLEAK_FREE(cachep); return 0; } /* Get the memory for a slab management obj. */ +#ifndef CONFIG_MEMLEAK static inline slab_t * kmem_cache_slabmgmt (kmem_cache_t *cachep, void *objp, int colour_off, int local_flags) +#else +static inline slab_t * kmem_cache_slabmgmt_wrap (kmem_cache_t *cachep, + void *objp, int colour_off, int local_flags, + struct alloc_struct *IDPTR) +#endif { slab_t *slabp; @@ -1067,7 +1100,12 @@ * Grow (by 1) the number of slabs within a cache. This is called by * kmem_cache_alloc() when there are no active objs left in a cache. */ +#ifndef CONFIG_MEMLEAK static int kmem_cache_grow (kmem_cache_t * cachep, int flags) +#else +static int kmem_cache_grow_wrap (kmem_cache_t * cachep, int flags, + struct alloc_struct *IDPTR) +#endif { slab_t *slabp; struct page *page; @@ -1288,7 +1326,12 @@ } #endif +#ifndef CONFIG_MEMLEAK static inline void * __kmem_cache_alloc (kmem_cache_t *cachep, int flags) +#else +static inline void * __kmem_cache_alloc_wrap (kmem_cache_t *cachep, + int flags, struct alloc_struct *IDPTR) +#endif { unsigned long save_flags; void* objp; @@ -1319,6 +1362,7 @@ #else objp = kmem_cache_alloc_one(cachep); #endif + MEMLEAK_ALLOC_NOLOCK(objp); local_irq_restore(save_flags); return objp; alloc_new_slab: @@ -1407,6 +1451,7 @@ slabp->free = objnr; } STATS_DEC_ACTIVE(cachep); + MEMLEAK_FREE(objp); /* fixup slab chain */ if (slabp->inuse-- == cachep->num) @@ -1503,7 +1548,12 @@ * Allocate an object from this cache. The flags are only relevant * if the cache has no available objects. */ +#ifndef CONFIG_MEMLEAK void * kmem_cache_alloc (kmem_cache_t *cachep, int flags) +#else +void * kmem_cache_alloc_wrap (kmem_cache_t *cachep, int flags, + struct alloc_struct *IDPTR) +#endif { return __kmem_cache_alloc(cachep, flags); } @@ -1529,7 +1579,11 @@ * * %GFP_KSWAPD - Don't use unless you're modifying kswapd. */ +#ifndef CONFIG_MEMLEAK void * kmalloc (size_t size, int flags) +#else +void * kmalloc_wrap (size_t size, int flags, struct alloc_struct *IDPTR) +#endif { cache_sizes_t *csizep = cache_sizes; @@ -1562,6 +1616,7 @@ local_irq_save(flags); __kmem_cache_free(cachep, objp); + MEMLEAK_FREE(cachep); local_irq_restore(flags); } diff -urN 2.4.0/mm/vmalloc.c 2.4.0-ikd-1/mm/vmalloc.c --- 2.4.0/mm/vmalloc.c Thu Dec 14 22:34:14 2000 +++ 2.4.0-ikd-1/mm/vmalloc.c Mon Jan 15 18:40:05 2001 @@ -6,10 +6,16 @@ * SMP-safe vmalloc/vfree/ioremap, Tigran Aivazian , May 2000 */ +#define MEMLEAK_PASS_ALLOCATION +#define MEMLEAK_UNWRAP_VMALLOC +#define MEMLEAK_UNWRAP_SLAB + +#include #include #include #include #include +#include #include #include @@ -17,6 +23,53 @@ rwlock_t vmlist_lock = RW_LOCK_UNLOCKED; struct vm_struct * vmlist; +#if defined(CONFIG_KDB) +/* kdb_vmlist_check + * Check to determine if an address is within a vmalloced range. + * Parameters: + * starta -- Starting address of region to check + * enda -- Ending address of region to check + * Returns: + * 0 -- [starta,enda] not within a vmalloc area + * 1 -- [starta,enda] within a vmalloc area + * Locking: + * None. + * Remarks: + * Shouldn't acquire locks. Always called with all interrupts + * disabled and other cpus halted. Yet, if a breakpoint or fault + * occurs while the vmlist is in an indeterminate state, this + * function could fail. + */ +int +kdb_vmlist_check(unsigned long starta, unsigned long enda) +{ + struct vm_struct *vp; + + if (vmlist) { + for(vp=vmlist; vp; vp = vp->next) { + unsigned long end = (unsigned long)vp->addr + vp->size; + + end -= PAGE_SIZE; /* Unbias for guard page */ + + if ((starta >= (unsigned long)vp->addr) + && (starta < end) + && (enda < end)) { + return 1; + } + } + } + else { + /* early kdb, no vmlist yet */ + extern char _text, _end; + if (starta >= (unsigned long) &_text && + enda < (unsigned long) &_end && + starta <= enda) + return 1; + } + return 0; +} +#endif + static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size) { pte_t * pte; @@ -90,8 +143,14 @@ flush_tlb_all(); } +#ifndef CONFIG_MEMLEAK static inline int alloc_area_pte (pte_t * pte, unsigned long address, unsigned long size, int gfp_mask, pgprot_t prot) +#else +static inline int alloc_area_pte_wrap (pte_t * pte, unsigned long address, + unsigned long size, int gfp_mask, pgprot_t prot, + struct alloc_struct *IDPTR) +#endif { unsigned long end; @@ -113,7 +172,13 @@ return 0; } +#ifndef CONFIG_MEMLEAK static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, int gfp_mask, pgprot_t prot) +#else +static inline int alloc_area_pmd_wrap(pmd_t * pmd, unsigned long address, + unsigned long size, int gfp_mask, pgprot_t prot, + struct alloc_struct *IDPTR) +#endif { unsigned long end; @@ -133,8 +198,14 @@ return 0; } -inline int vmalloc_area_pages (unsigned long address, unsigned long size, - int gfp_mask, pgprot_t prot) +#ifndef CONFIG_MEMLEAK +inline int vmalloc_area_pages (unsigned long address, + unsigned long size, int gfp_mask, pgprot_t prot) +#else +inline int vmalloc_area_pages_wrap (unsigned long address, + unsigned long size, int gfp_mask, pgprot_t prot, + struct alloc_struct *IDPTR) +#endif { pgd_t * dir; unsigned long end = address + size; @@ -165,7 +236,11 @@ return ret; } +#ifndef CONFIG_MEMLEAK struct vm_struct * get_vm_area(unsigned long size, unsigned long flags) +#else +struct vm_struct * get_vm_area_wrap(unsigned long size, unsigned long flags, struct alloc_struct *IDPTR) +#endif { unsigned long addr; struct vm_struct **p, *tmp, *area; @@ -224,7 +299,12 @@ printk(KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n", addr); } +#ifndef CONFIG_MEMLEAK void * __vmalloc (unsigned long size, int gfp_mask, pgprot_t prot) +#else +void * __vmalloc_wrap (unsigned long size, int gfp_mask, pgprot_t prot, + struct alloc_struct *IDPTR) +#endif { void * addr; struct vm_struct *area; diff -urN 2.4.0/net/core/skbuff.c 2.4.0-ikd-1/net/core/skbuff.c --- 2.4.0/net/core/skbuff.c Tue Jan 2 17:41:24 2001 +++ 2.4.0-ikd-1/net/core/skbuff.c Mon Jan 15 18:40:05 2001 @@ -36,6 +36,10 @@ * The functions in this file will not compile correctly with gcc 2.4.x */ +#define MEMLEAK_PASS_ALLOCATION +#define MEMLEAK_UNWRAP_SKBUFF +#define MEMLEAK_UNWRAP_SLAB + #include #include #include @@ -62,6 +66,8 @@ #include #include +#include + int sysctl_hot_list_len = 128; static kmem_cache_t *skbuff_head_cache; @@ -162,7 +168,12 @@ * %GFP_ATOMIC. */ +#ifndef CONFIG_MEMLEAK struct sk_buff *alloc_skb(unsigned int size,int gfp_mask) +#else +struct sk_buff *alloc_skb_wrap(unsigned int size,int gfp_mask, + struct alloc_struct * IDPTR) +#endif { struct sk_buff *skb; u8 *data; @@ -306,7 +317,12 @@ * %GFP_ATOMIC. */ +#ifndef CONFIG_MEMLEAK struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask) +#else +struct sk_buff *skb_clone_wrap(struct sk_buff *skb, int gfp_mask, + struct alloc_struct * IDPTR) +#endif { struct sk_buff *n; @@ -385,7 +401,12 @@ * is called from an interrupt. */ +#ifndef CONFIG_MEMLEAK struct sk_buff *skb_copy(const struct sk_buff *skb, int gfp_mask) +#else +struct sk_buff *skb_copy_wrap(const struct sk_buff *skb, int gfp_mask, + struct alloc_struct * IDPTR) +#endif { struct sk_buff *n; @@ -429,10 +450,18 @@ */ +#ifndef CONFIG_MEMLEAK struct sk_buff *skb_copy_expand(const struct sk_buff *skb, int newheadroom, int newtailroom, int gfp_mask) +#else +struct sk_buff *skb_copy_expand_wrap(const struct sk_buff *skb, + int newheadroom, + int newtailroom, + int gfp_mask, + struct alloc_struct * IDPTR) +#endif { struct sk_buff *n; diff -urN 2.4.0/net/core/sock.c 2.4.0-ikd-1/net/core/sock.c --- 2.4.0/net/core/sock.c Tue Jan 2 17:41:24 2001 +++ 2.4.0-ikd-1/net/core/sock.c Mon Jan 15 18:40:05 2001 @@ -89,6 +89,11 @@ * 2 of the License, or (at your option) any later version. */ +#define MEMLEAK_PASS_ALLOCATION +#define MEMLEAK_UNWRAP_SOCK +#define MEMLEAK_UNWRAP_SKBUFF +#define MEMLEAK_UNWRAP_SLAB + #include #include #include @@ -129,6 +134,8 @@ #include #endif +#include + #define min(a,b) ((a)<(b)?(a):(b)) /* Run time adjustable parameters. */ @@ -569,7 +576,11 @@ * usage. */ +#ifndef CONFIG_MEMLEAK struct sock *sk_alloc(int family, int priority, int zero_it) +#else +struct sock *sk_alloc_wrap(int family, int priority, int zero_it, struct alloc_struct *IDPTR) +#endif { struct sock *sk = kmem_cache_alloc(sk_cachep, priority); @@ -651,10 +662,14 @@ atomic_sub(skb->truesize, &sk->rmem_alloc); } +#ifndef CONFIG_MEMLEAK /* * Allocate a skb from the socket's send buffer. */ struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, int priority) +#else +struct sk_buff *sock_wmalloc_wrap(struct sock *sk, unsigned long size, int force, int priority, struct alloc_struct *IDPTR) +#endif { if (force || atomic_read(&sk->wmem_alloc) < sk->sndbuf) { struct sk_buff * skb = alloc_skb(size, priority); @@ -666,10 +681,14 @@ return NULL; } +#ifndef CONFIG_MEMLEAK /* * Allocate a skb from the socket's receive buffer. */ struct sk_buff *sock_rmalloc(struct sock *sk, unsigned long size, int force, int priority) +#else +struct sk_buff *sock_rmalloc_wrap(struct sock *sk, unsigned long size, int force, int priority, struct alloc_struct *IDPTR) +#endif { if (force || atomic_read(&sk->rmem_alloc) < sk->rcvbuf) { struct sk_buff *skb = alloc_skb(size, priority); @@ -681,10 +700,14 @@ return NULL; } +#ifndef CONFIG_MEMLEAK /* * Allocate a memory block from the socket's option memory buffer. */ void *sock_kmalloc(struct sock *sk, int size, int priority) +#else +void *sock_kmalloc_wrap(struct sock *sk, int size, int priority, struct alloc_struct *IDPTR) +#endif { if ((unsigned)size <= sysctl_optmem_max && atomic_read(&sk->omem_alloc)+size < sysctl_optmem_max) { @@ -742,8 +765,13 @@ * Generic send/receive buffer handlers */ +#ifndef CONFIG_MEMLEAK struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigned long fallback, int noblock, int *errcode) +#else +struct sk_buff *sock_alloc_send_skb_wrap(struct sock *sk, unsigned long size, + unsigned long fallback, int noblock, int *errcode, struct alloc_struct *IDPTR) +#endif { int err; struct sk_buff *skb; diff -urN 2.4.0/net/netsyms.c 2.4.0-ikd-1/net/netsyms.c --- 2.4.0/net/netsyms.c Thu Nov 16 15:37:43 2000 +++ 2.4.0-ikd-1/net/netsyms.c Mon Jan 15 18:40:05 2001 @@ -109,10 +109,17 @@ EXPORT_SYMBOL(sock_getsockopt); EXPORT_SYMBOL(sock_sendmsg); EXPORT_SYMBOL(sock_recvmsg); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(sk_alloc); +#endif EXPORT_SYMBOL(sk_free); EXPORT_SYMBOL(sock_wake_async); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(sock_alloc_send_skb); +#else +EXPORT_SYMBOL(sk_alloc_wrap); +EXPORT_SYMBOL(sock_alloc_send_skb_wrap); +#endif EXPORT_SYMBOL(sock_init_data); EXPORT_SYMBOL(sock_no_release); EXPORT_SYMBOL(sock_no_bind); @@ -131,16 +138,27 @@ EXPORT_SYMBOL(sock_no_mmap); EXPORT_SYMBOL(sock_rfree); EXPORT_SYMBOL(sock_wfree); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(sock_wmalloc); EXPORT_SYMBOL(sock_rmalloc); +#else +EXPORT_SYMBOL(sock_wmalloc_wrap); +EXPORT_SYMBOL(sock_rmalloc_wrap); +#endif EXPORT_SYMBOL(skb_recv_datagram); EXPORT_SYMBOL(skb_free_datagram); EXPORT_SYMBOL(skb_copy_datagram); EXPORT_SYMBOL(skb_copy_datagram_iovec); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(skb_copy_expand); +#endif EXPORT_SYMBOL(datagram_poll); EXPORT_SYMBOL(put_cmsg); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(sock_kmalloc); +#else +EXPORT_SYMBOL(sock_kmalloc_wrap); +#endif EXPORT_SYMBOL(sock_kfree_s); #ifdef CONFIG_FILTER @@ -474,10 +492,19 @@ #if 0 EXPORT_SYMBOL(eth_copy_and_sum); #endif +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(alloc_skb); +#endif EXPORT_SYMBOL(__kfree_skb); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(skb_clone); EXPORT_SYMBOL(skb_copy); +#else +EXPORT_SYMBOL(alloc_skb_wrap); +EXPORT_SYMBOL(skb_clone_wrap); +EXPORT_SYMBOL(skb_copy_wrap); +EXPORT_SYMBOL(skb_copy_expand_wrap); +#endif EXPORT_SYMBOL(netif_rx); EXPORT_SYMBOL(dev_add_pack); EXPORT_SYMBOL(dev_remove_pack); diff -urN 2.4.0/scripts/Makefile 2.4.0-ikd-1/scripts/Makefile --- 2.4.0/scripts/Makefile Thu Mar 16 20:27:17 2000 +++ 2.4.0-ikd-1/scripts/Makefile Mon Jan 15 18:40:05 2001 @@ -1,6 +1,20 @@ HEADER=header.tk TAIL=tail.tk +# +# include dependency files they exist +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + +# +# Routines in this directory are external to the kernel but partake of the +# kernel namespace. Since they are external, they are not candidates for +# profiling. +# +override CFLAGS := $(CFLAGS:%-pg=%-g -c) + # Previous versions always remade kconfig.tk because they always depended # on soundscript. This runs fairly fast, and I can't find all the # Config.in files to depend on anyways. So I'll force it to remake. @@ -33,6 +47,9 @@ tkparse.o tkcond.o tkgen.o: $(HOSTCC) $(HOSTCFLAGS) -c -o $@ $(@:.o=.c) +ktrace: ktrace.o + $(CC) -o ktrace ktrace.o + docproc.o: docproc.c $(HOSTCC) $(HOSTCFLAGS) -c -o $@ $(@:.o=.c) @@ -41,5 +58,6 @@ clean: rm -f *~ kconfig.tk *.o tkparse mkdep split-include docproc + rm -f ktrace include $(TOPDIR)/Rules.make diff -urN 2.4.0/scripts/ktrace.c 2.4.0-ikd-1/scripts/ktrace.c --- 2.4.0/scripts/ktrace.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/scripts/ktrace.c Mon Jan 15 18:40:05 2001 @@ -0,0 +1,481 @@ +/* ktrace.c + * + * Read /proc/trace and System.map (or equivalent) and print the trace entries. + * Prints the time taken between trace calls, "(????)" if the next entry for the + * current processor cannot be found. Prints the current pid, if the next entry + * for the current processor is for a different pid, prints "pid(old->new)". + * If compiled for SMP, the trace table contains the logical processor number, + * this is printed as "cpu(n)". + * + * The System.map can be the standard System.map for the kernel, in which case + * module traces will not resolve very well. It can be a merged System.map + * containing module entries as well, see make_System_map.pl for an example, + * ftp://ftp.ocs.com.au/pub/. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_TRACE +#include + +/* + * Dumbomarbitrary limits + */ + +#define LINE_LIMIT 100 +#define SYSMAP_LIMIT 30000 + +static struct trace_table * tt; + +struct sysmap_entry { + profiler_pc_t pc; + char * name; +}; + +static struct sysmap_entry sysmap_table [SYSMAP_LIMIT]; + +static int sysmap_entries = 0; +static int default_speed = 150, speed, lock = 0; +static char *default_map = "/usr/src/linux/System.map", *map, *dump; +static char *prog_name; + +static void usage(void) +{ + fprintf(stderr, "usage: %s\n", prog_name); + fprintf(stderr, "\t[--speed MHz] [-s MHz]\t\t\thow fast is your processor?\n"); + fprintf(stderr, "\t[--map System.map] [-m System.map]\twhere is your system map?\n"); + fprintf(stderr, "\t[--lock] [-l]\t\t\t\twait for the lock on /proc/trace\n"); + fprintf(stderr, "\t[--dump filename] [-d filename]\t\tread trace dump from filename\n"); + fprintf(stderr, "Default --speed is %d\n", default_speed); + fprintf(stderr, "Default --map is %s\n", default_map); + exit(1); +} + +static void read_sysmap (void) +{ + profiler_pc_t pc; + char type; + int i, len; + + FILE * file; + char str [LINE_LIMIT+1]; + + file=fopen(map, "r"); + + if (!file) { + fprintf(stderr,"System.map '%s' missing.\n", map); + usage(); + } + + sysmap_table[0].pc = 0; + sysmap_table[0].name = "
\n"; + + sysmap_entries=1; + + while (fscanf(file, "%lx %1c", &pc, &type) == 2) { + i=sysmap_entries++; + if (!fgets(str, LINE_LIMIT, file)) { + perror("ouch, System.map format error.\n"); + exit(-1); + } + sysmap_table[i].pc = pc; + sysmap_table[i].name = malloc(LINE_LIMIT); + if (!sysmap_table[i].name) { + perror("ouch, outta mem.\n"); + exit(-1); + } + /* + * Dirty trick to strip off end of line: + */ + len = strlen(str); + str[len-1]=0; + strcpy (sysmap_table[i].name, str); + } + + printf("read %d lines from System.map.\n", sysmap_entries-1); + + sysmap_table[sysmap_entries].pc = ~1; + sysmap_table[sysmap_entries].name = "
\n"; + sysmap_entries++; + + /* To be sure, to be sure :). */ + sysmap_table[sysmap_entries].pc = ~0; + sysmap_table[sysmap_entries++].name = ""; + sysmap_table[sysmap_entries].pc = ~0; + sysmap_table[sysmap_entries++].name = ""; + +/* + * for (i=0; i1) { + middle = first+(last-first)/2; + if (sysmap_table[middle].pc <= pc) + first = middle; + else + last = middle; + } + + return first; +} + +/* The trace table is a ring buffer. Convert 0 <= index < size to the + * corresponding entry, with wraparound as necessary. + */ +static inline int ring(int x) +{ + return ((x) % CONFIG_TRACE_SIZE); +} + +#if defined(CONFIG_TRACE_CPU) && defined(CONFIG_SMP) +#define CPU_PRESENT 1 +#else +#define CPU_PRESENT 0 +#endif + +static ssize_t read_dump(int fd, void *buf, size_t count) +{ + /* Find the start of the hex dump of /proc/trace, read + * and convert hex digits, storing in buf. Any garbage + * nibbles are silently ignored and treated as '0'. + */ + char line[BUFSIZ]; + int start = 0, value; + char *pline, c; + unsigned char *pbuf; + FILE *f = fdopen(fd, "r"); + if (!f) { + perror("read_dump fdopen failed"); + exit(-1); + } + pbuf = (unsigned char *) buf; + while (fgets(line, sizeof(line), f)) { + if (ferror(f)) { + perror("read_dump ferror detected"); + exit(-1); + } + if (strstr(line, "DAL: ktrace start")) { + start = 1; + continue; + } + if (start) { + if (strstr(line, "DAL: ktrace end")) + break; + pline = line; + while (*pline) { + while (*pline == '\r' || *pline == '\n') + ++pline; + if (!(c = *pline++)) + break; + value = 0; + if (c >= '0' && c <= '9') + value = c - '0'; + else if (c >= 'a' && c <= 'f') + value = c - 'a' + 10; + value <<= 4; + if (!(c = *pline++)) + break; + if (c >= '0' && c <= '9') + value += c - '0'; + else if (c >= 'a' && c <= 'f') + value += c - 'a' + 10; + if (count > 0) { + --count; + *(pbuf++) = (unsigned char) value; + } + if (count == 0) + break; + } + } + } + return(pbuf - (unsigned char *)buf); +} + +static void read_proc_info (void) +{ + int bytes, calibrate; + int i, j; +#ifdef CONFIG_TRACE_TIMESTAMP + profiler_timestamp_t min_latency; +#endif + struct trace_entry *tep1 = NULL, *tep2 = NULL; + + char *filename = "/proc/trace"; + int file; + + if (dump) + filename = dump; + + file=open(filename, O_RDONLY); + + if (!file) { + char message[BUFSIZ]; + sprintf(message, "%s missing\n", filename); + perror(message); + exit(-1); + } + if (lock && !dump && flock(file, LOCK_EX)) { + char message[BUFSIZ]; + sprintf(message, "Cannot get exclusive lock on %s\n", filename); + perror(message); + exit(-1); + } + + tt=(struct trace_table *)malloc(sizeof(*trace_table)); + + if (dump) { + printf("Reading dumped /proc/trace from %s ...", dump); + fflush(stdout); + bytes = read_dump(file, tt, sizeof(*trace_table)); + printf(" done\n"); + fflush(stdout); + } + else + bytes = read(file, tt, sizeof(*trace_table)); + + if (sizeof(*trace_table) != bytes) { + printf("something went wrong, bytes read: %d, tried: %d.\n", bytes, sizeof(*trace_table)); + exit(-1); + } + + if (lock && !dump && flock(file, LOCK_UN)) { + char message[BUFSIZ]; + sprintf(message, "Release lock on %s failed\n", filename); + perror(message); + } + + /* + * Pass 1: look for ~0 which signals calibration latencies. + * Since read_trace (fs/proc/array.c) locks the table and turns + * off mcount processing, the calibration entries should be the + * current entry and the previous TRACE_CALIBRATION_CALLS-1. + */ +#define FIRST_CALIBRATE (tt->curr_call-(TRACE_CALIBRATION_CALLS-1)) + +#ifdef CONFIG_TRACE_TIMESTAMP + min_latency = ~0; +#endif + calibrate = 0; + + if (!dump) { + /* look for read_trace in 200 entries before FIRST_CALIBRATE. + * 200 is arbitrary, normally read_trace is immediately before + * the first calibration but there is a small window between + * read_trace starting and tracing being suspended, other cpu's + * and/or interrupts can appear in that window. KAO + */ + for (j = 1; j <= 200; ++j) { + tep1 = &(tt->entries[ring(FIRST_CALIBRATE-j)]); + i = match_pc(tep1->pc); + if (!strcmp(sysmap_table[i].name," read_trace")) + break; + } + if (strcmp(sysmap_table[i].name," read_trace")) { + tep1 = &(tt->entries[ring(FIRST_CALIBRATE-1)]); + i = match_pc(tep1->pc); + fprintf(stderr, + "hmm, no 'read_trace', possibly wrong System.map?.\npc %lx proc %s\n", + tep1->pc, sysmap_table[i].name); + } + } + + for (i = FIRST_CALIBRATE; i < tt->curr_call; i++) { + tep1 = &(tt->entries[ring(i)]); + tep2 = &(tt->entries[ring(i+1)]); + if (tep1->pc == ~0 && tep2->pc == ~0) { +#ifdef CONFIG_TRACE_TIMESTAMP + profiler_timestamp_t delta; + delta = tep2->timestamp - tep1->timestamp; + if (delta < min_latency) + min_latency=delta; +#endif /* CONFIG_TRACE_TIMESTAMP */ + ++calibrate; + } + } + + if (calibrate != TRACE_CALIBRATION_CALLS-1) { + fprintf(stderr,"huh, incorrect number of calibration entries found (%d)?.\n", calibrate); +#ifdef CONFIG_TRACE_TIMESTAMP + fprintf(stderr,"using 0.13 usecs.\n"); /*MIKEDIDIT was .39 (p5-150?)*/ + min_latency = 0.13*speed; + } else { + printf("calibration done, estimated measurement latency: %3.2f microseconds.\n", min_latency/(double)speed); + if (min_latency == 0) { + printf("Warning: latency is zero, does your cpu really support timestamps?\n"); + } + else + min_latency -= 10; +#endif /* CONFIG_TRACE_TIMESTAMP */ + } + printf("\n"); + + + /* Pass 2. */ + + for (i = 1; i <= CONFIG_TRACE_SIZE; i++) { + unsigned int idx; +#ifdef CONFIG_TRACE_TIMESTAMP + profiler_timestamp_t delta = -1; +#endif /* CONFIG_TRACE_TIMESTAMP */ + + tep1 = &(tt->entries[ring(tt->curr_call+i)]); + if (tep1->pc == 0) + continue; /* trace table has been cleared */ +#ifdef CONFIG_TRACE_TIMESTAMP +#if CPU_PRESENT + for (j = 1; j <= CONFIG_TRACE_SIZE-i; ++j) { + tep2 = &(tt->entries[ring(tt->curr_call+i+j)]); + if (tep2->pc == 0) + break; + if (tep1->cpu == tep2->cpu) { + delta = tep2->timestamp - tep1->timestamp; + break; + } + } +#else /* CPU_PRESENT */ + tep2 = &(tt->entries[ring(tt->curr_call+i+1)]); + if (tep2->pc != 0 && i < CONFIG_TRACE_SIZE) + delta = tep2->timestamp - tep1->timestamp; +#endif /* CPU_PRESENT */ +#endif /* CONFIG_TRACE_TIMESTAMP */ + + idx = match_pc(tep1->pc); + +#if 0 /* testing only */ +#ifdef CONFIG_TRACE_TIMESTAMP +#ifdef CONFIG_TRACE_TRUNCTIME + printf("%08x ", tep1->timestamp); +#else + printf("%08llx%08llx ", tep1->timestamp >> 32, + tep1->timestamp & 0xffffffff); +#endif +#endif /* CONFIG_TRACE_TIMESTAMP */ +#endif + printf("%08lx %s +<%lx/%lx>", + tep1->pc, + sysmap_table[idx].name, + tep1->pc-sysmap_table[idx].pc, + sysmap_table[idx+1].pc - sysmap_table[idx].pc); +#ifdef CONFIG_TRACE_TIMESTAMP + if (delta == -1) + printf(" (????)"); + else if (tep1->pc == ~0) + printf(" (%3.08f raw)", + (double)delta); + else + printf(" (%3.02f)", + (delta-min_latency)/(double)speed); +#endif /* CONFIG_TRACE_TIMESTAMP */ +#if CPU_PRESENT + printf(" cpu(%d)", tep1->cpu); +#endif +#ifdef CONFIG_TRACE_PID + if (tep1->pid == tep2->pid) + printf(" pid(%d)", tep1->pid); + else + printf(" pid(%d->%d)", tep1->pid, tep2->pid); +#endif /* CONFIG_TRACE_PID */ + printf("\n"); + } + + free(tt); + close(file); + + printf("\n"); +} + +int main(int argc, char * * argv) +{ + int c, option_index = 0; + char *endptr; + struct option long_options[] = { + {"speed", 1, 0, 's'}, + {"map", 1, 0, 'm'}, + {"lock", 0, 0, 'l'}, + {"dump", 1, 0, 'd'}, + {0, 0, 0, 0} + }; + + prog_name = argv[0]; + speed = default_speed; + map = default_map; + + while (1) { + c = getopt_long_only (argc, argv, "s:m:ld:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 's': + speed = strtol(optarg, &endptr, 0); + if (*endptr) { + fprintf(stderr, "speed is not numeric '%s'\n", + optarg); + usage(); + } + if (speed < 0 || speed > 1000) { + fprintf(stderr, "speed must be 1-1000\n"); + usage(); + } + break; + + case 'm': + map = optarg; + break; + + case 'l': + lock = !lock; + break; + + case 'd': + dump = optarg; + break; + + case '?': + usage(); + exit(-1); + + default: + printf ("?? getopt returned character code 0%o '%c' ??\n", c, c); + } + } + + if (optind < argc) { + fprintf (stderr, "Unknown parameter '%s'\n", argv[optind]); + usage(); + exit(-1); + } + + printf("Speed: %d. Map: %s\n", speed, map); + + read_sysmap(); + read_proc_info(); + return 0; +} + +#else +#warning ktrace does nothing unless CONFIG_TRACE is set +int main(void) { return 0; } +#endif /* CONFIG_TRACE */ diff -urN 2.4.0/scripts/lockstat/Makefile 2.4.0-ikd-1/scripts/lockstat/Makefile --- 2.4.0/scripts/lockstat/Makefile Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/scripts/lockstat/Makefile Mon Jan 15 18:40:05 2001 @@ -0,0 +1,23 @@ +# Makefile for lockstat + +# LINUX_INC_ROOT is the root of the include directory of the Linux tree +# for which you want to compile. If you want to compile for a Linux version +# other than the one currently installed on your machine, define it in +# your environment or on the make command line. +ifndef LINUX_INC_ROOT +LINUX_INC_ROOT = ../../include +endif + +PROGS = lockstat +CFLAGS := -O2 -fomit-frame-pointer -I$(LINUX_INC_ROOT) -U__KERNEL__ + +default all: $(PROGS) + +lockstat: lockstat.o + $(CC) -o $@ $< + +install: lockstat + install -m 0755 lockstat $(INSTALLROOT)/usr/sbin + +clean: + $(RM) $(PROGS) *.o diff -urN 2.4.0/scripts/lockstat/lockstat.c 2.4.0-ikd-1/scripts/lockstat/lockstat.c --- 2.4.0/scripts/lockstat/lockstat.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/scripts/lockstat/lockstat.c Mon Jan 15 18:40:05 2001 @@ -0,0 +1,1139 @@ +/* + * lockstat.c - control kernel lock metering + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on lockstat.c by Jack Steiner (steiner@sgi.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +#include + +#ifdef TIMER +extern void cycletrace(char*); +#define TSTAMP(s) cycletrace(s); +#else +#define TSTAMP(s) +#endif + +#define HZ 100 + +char *helpstr[] = { +"Name" +" lockstat - display kernel lock statistics", +"", +"SYNOPSIS", +" lockstat on/hold/off", +" lockstat [ ...] command [args...]", +" lockstat [ ...] []", +"", +"DESCRIPTION", +" This command will display statistics on the number of times kernel locks", +" are set and the amount on contention that occurs for each lock.", +"", +" The first form of the command is used to turn lock statistics collection", +" on or off. The default 'on' state records and reports only spinlock", +" 'wait' times. The 'hold' directive records and reports both 'wait'", +" and 'hold' times. The 'off' directive turns off kernel recording.", +" The second form of the command is used to collect lock statistics during", +" execution of the specified command.", +" The third form if the command is used to periodically sample lock ", +" statistics. The sample time is specified by and the sample may", +" be repeated times.", +"", +"OPTIONS", +" -m Location of map file to use for symbol table. If not", +" specified, the value /usr/src/linux/System.map is used.", +" -i String to print as part of the title for the output.", +" -c By default, the statistics for all cpus are summed", +" together. This argument specifies individual cpus that", +" should be reported separately.", +" -t Normally, incremental statistics are reported. If -t", +" is specified, total counts since lockstats were enabled", +" is reported.", +#ifdef notyet +" -S Show semaphore information. This is not selected by default", +" because I am not sure it is useful.", +#endif +" -p Report only on locks set more than ", +" times per second.", +" -k Report only on locks with more than contention.", +" -w Report on \"warm or hot locks\" only. This option reports on", +" all locks with any contention.", +" -h Report on \"hot locks\" only. This option is the same as", +" selecting: -p 100 -k 5", +"", +"REPORT", +" The report is divided into several sections:", +" SPINLOCKS", +#ifdef notyet +" MRLOCKS", +#endif +" RWLOCK SPINLOCK (MultipleReader-SingleWriter spinlocks)", +#ifdef notyet +" SEMAPHORES (if -S is selected)", +#endif +"", +" The following data is cellected for each lock:", +"", +" TOT/SEC Number of times per second that the lock was set.", +" CON Amount of contention that occurred for the lock. The ", +" number represents the percent of time that lock was NOT", +" acquired without spinning or sleeping.", +#ifdef notyet +" Note: for semaphores, the number represents the % of", +" the time psema slept.", +#endif +" HOLD Mean spin-wait hold time, in microseconds.", +" WAIT Mean spin-wait wait time, in microseconds.", +" TOTAL Total number of times that the lock was set.", +" NOWAIT Number of times the lock was acquired without waiting", +" SPIN Number of times it was necessary to spin waiting for", +" a spinlock.", +#ifdef notyet +" SLEEP Number of times it was necessary to sleep for a lock", +#endif +" REJECT Number of time a \"trylock\" failed.", +" NAME Identifies the lock and/or the routine that set the lock.", +" If the the is statically defined and not part of an array,", +" both the lock name and the functions that set the ", +" lock are listed. If the lock is dynamically allocated,", +" only the function name that set the lock will be listed.", +"", +NULL}; + +static char defaultDataFilename[] = "/proc/lockmeter"; +int dataFile; +char *general_read_buf; +int general_read_buf_len; + +static char defaultMapFilename[] = "/usr/src/linux/System.map"; +FILE *mapFile; + +#define MAXCPUS 512 +#define STRMAX 100 +#define SPACEINC 16384*4 + +#define perrorx(s) perror(s), exit(1) +#define fatalx(m) fprintf(stderr, "ERROR - %s\n", m), exit(1) +#define notex(m) fprintf(stderr, "%s\n", m), exit(0) +#define max(a,b) (((a)<(b)) ? (b) : (a)) +#define min(a,b) (((a)>(b)) ? (b) : (a)) + + +typedef enum {Buf_Previous, Buf_Current} get_data_buffer_enum ; +typedef enum {Null_Entry, Special_Entry, Normal_Entry, Mr_Entry, RWspin_Entry, Sema_Entry} entry_type_enum; + +typedef struct { + lstat_lock_counts_t counts; + uint32_t total; + double contention; + double persec; +} lock_summary_t; + +typedef struct { + void *caller_ra; + char *caller_name; + char *lock_name; + char multilock; + char title_index; + char caller_name_len; + entry_type_enum entry_type; +} directory_entry_t; + +directory_entry_t directory[LSTAT_MAX_STAT_INDEX]; +int next_free_dir_index; + +lstat_directory_entry_t kernel_directory[LSTAT_MAX_STAT_INDEX]; +lstat_directory_entry_t prev_directory[LSTAT_MAX_STAT_INDEX]; + +lstat_cpu_counts_t *kernel_counts; +lstat_cpu_counts_t *prev_counts; +lstat_lock_counts_t total_counts[LSTAT_MAX_STAT_INDEX]; +short sorti[LSTAT_MAX_STAT_INDEX+2]; + +int numcpus = 0; +int enabled; +float cycles_per_usec; +void *kernel_magic_addr; +void *kernel_end_addr; +time_t start_time, end_time; +double deltatime; +int skipline = 0; +char *current_header, *last_header; + +char *dashes = "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n"; + +char *special_titles[] = { + "SPINLOCKS HOLD WAIT\n" + " TOT/SEC CON MEAN ( MAX ) MEAN ( MAX ) TOTAL NOWAIT SPIN REJECT NAME", /* normal */ + "RWLOCK SPINLOCK WAIT\n" + " TOT/SEC CON MEAN ( MAX ) TOTAL NOWAIT SPIN REJECT NAME", /* normal */ + "SEMAPHORES \n TOT/SEC CON TOTAL NOWAIT SPIN SLEEP REJECT NAME", /* sema */ +#ifdef notyet + "MRLOCKS\n TOT/SEC CON WAIT TOTAL NOWAIT SPIN SLEEP REJECT NAME", /* normal */ +#endif + ""}; + + +void set_header(char*); +void enable_statistic_collection(int); +void do_report(char *, int); +void print_stats(char *, char *); +void set_header(char *); +void reset_counts(lock_summary_t *); +void add_counts(lock_summary_t *, lstat_lock_counts_t *); +int sum_counts(lock_summary_t *, entry_type_enum); +int set_counts(lock_summary_t *, lstat_lock_counts_t *, entry_type_enum); +void do_help(void); +void print_header(void); +void print_title(char *, char *); +void print_lock(char *, lock_summary_t *, int, entry_type_enum); +void get_collection_state(int); +void get_kernel_data(get_data_buffer_enum); +void build_sort_directory(void); +int sortcmp(const void *, const void *); +void sum_data (int, int); +int loadsymtab (char *, void *, void *); +void myaddress_to_symbol(void *, char *); +char* strspace(void); +int read_diff_file(void); +void write_diff_file(void); + + +extern int optind, opterr, errno; +extern char *optarg; + +int semaopt = 0, topt = 0, debugopt = 0, opt_debug = 0; +double opt_contention = -1.0, opt_persec = 0.0; +char *debugname=NULL; + +char *ident = 0; + +char cpulist[MAXCPUS]; + +int +main(int argc, char **argv) +{ +#ifdef notyet + static char optstr[] = "Sfwdhk:p:tc:i:m:D:"; +#endif + static char optstr[] = "fwdhk:p:tc:i:m:D:"; + int c, err = 0; + int args, cpunum; + char title[120]; + char *dataFilename; + char *mapFilename; + + dataFilename = defaultDataFilename; + mapFilename = defaultMapFilename; + + while ((c = getopt(argc, argv, optstr)) != EOF) + switch (c) { + case 'D': + debugopt = 1; + debugname = optarg; + break; + case 'c': + if (*optarg == 'a') { + for (cpunum = 0; cpunum< MAXCPUS; cpunum++) + cpulist[cpunum]++; + } else { + cpunum = atoi(optarg); + if (cpunum < 0 || cpunum >= MAXCPUS) + fatalx("invalid cpu number specified"); + cpulist[cpunum]++; + } + break; + case 'd': + opt_debug = 1; + break; + case 'w': + opt_persec = 0.0; + opt_contention = 0.0; + break; + case 'h': + opt_persec = 100.0; + opt_contention = 5.0; + break; + case 'p': + opt_persec = (double)atoi(optarg); + break; + case 'k': + opt_contention = (double)atoi(optarg); + break; + case 'i': + ident = optarg; + break; +#ifdef notyet + case 'S': + semaopt++; + break; +#endif + case 't': + topt++; + break; + case 'm': + mapFilename = optarg; + break; + case '?': + err = 1; + break; + } + + setuid(0); /* try to become root */ + if ((dataFile = open(dataFilename, O_RDWR)) == -1) + { + perror(dataFilename); + exit(1); + } + + TSTAMP("start"); + if (debugopt && read_diff_file()) + debugopt = 2; + else + get_collection_state(0); + + TSTAMP("inited"); + args = argc - optind; + if (err || args < 0) + fatalx("invalid arguments specified"); + + if (!args && !topt) { + do_help(); + return(0); + } + + + if (args == 1) { + if (strcmp(argv[optind], "on") == 0) { + enable_statistic_collection (LSTAT_ON); + return(0); + +#if 0 + } else if (strcmp(argv[optind], "hold") == 0) { + enable_statistic_collection (LSTAT_ON_HOLDS); + return(0); + +#endif + } else if (strcmp(argv[optind], "off") == 0) { + enable_statistic_collection (LSTAT_OFF); + return(0); + } + } + + if (!enabled) + fatalx ("lockstat collection not enabled"); + + if ((mapFile = fopen(mapFilename,"r")) == NULL) { + fprintf(stderr, "Cannot open mapfile '%s'\n", mapFilename); + exit(1); + } + + if (topt && !args || isdigit(*argv[optind])) { + int i, sleepsec=0, sleepcnt=1; + if (args) { + sleepsec = atoi(argv[optind]); + sleepcnt = (args>1) ? atoi(argv[optind+1]) : 1; + } + for (i=1; i<= sleepcnt; i++) { + if (i > 1) + fprintf(stderr, "\n\n"); + if (!topt) + get_kernel_data (Buf_Previous); + if (sleepsec) + sleep(sleepsec); + get_kernel_data (Buf_Current); + if (topt) + sprintf(title, "Total counts since lock statistics were enabled\n"); + else + sprintf(title, "Periodic sample %d of %d. Sample period: %d secs\n", + i, sleepcnt, sleepsec); + do_report(title, (i == sleepcnt)); + } + + } else { + int pid, stat; + + get_kernel_data (Buf_Previous); + if ((pid=fork()) == 0) { + execvp(argv[optind], &argv[optind]); + perrorx("unable to exec command"); + exit(1); + } else if (pid < 0) + perrorx("fork failed"); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + while (wait(&stat) != pid); + if((stat&0377) != 0) + fprintf(stderr,"Command terminated abnormally.\n"); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + get_kernel_data (Buf_Current); + strcpy(title, "Command: "); + for (; optind < argc; optind++) { + if(5+strlen(title)+strlen(argv[optind]) > sizeof(title)) { + strcat(title, " ..."); + break; + } + strcat(title, argv[optind]); + strcat(title, " "); + } + do_report(title, 1); + } + + TSTAMP("done"); + return(0); +} + + +void +do_help(void) +{ + char **p; + + for (p=helpstr; *p; p++) + printf("%s\n", *p); +} + +void +do_report(char *title, int last_report) +{ + int cpu; + char name[100]; + + TSTAMP("start do report"); + build_sort_directory(); + + TSTAMP("finish sort"); + sum_data(0, numcpus - 1); + sprintf(name, "All (%d) CPUs", numcpus); + print_stats(title, name); + + for (cpu = 0; cpu < numcpus; cpu++) { + if (cpulist[cpu] == 0) + continue; + sum_data(cpu, cpu); + sprintf(name, "CPU:%d", cpu); + print_stats(title, name); + } + if (last_report) + fprintf (stderr, "___________________________________________________________________________________________\n"); + fflush(stderr); + TSTAMP("end do report"); +} + + +void +print_stats(char *title1, char *title2) +{ + entry_type_enum entry_type, current_entry_type = Null_Entry; + lock_summary_t sum_count; + int i, j, k, si, sj; + + + print_title(title1, title2); + + for (i = 1; i < next_free_dir_index; i++) { + si = sorti[i]; + entry_type = directory[si].entry_type; + if (entry_type == Sema_Entry && !semaopt) + continue; + set_header (special_titles[directory[si].title_index]); + + if (entry_type != current_entry_type) { + current_entry_type = entry_type; + reset_counts (&sum_count); + for (j = i; j < next_free_dir_index; j++) { + sj = sorti[j]; + if (directory[sj].entry_type != entry_type) + break; + add_counts (&sum_count, &total_counts[sj]); + } + sum_counts (&sum_count, entry_type); + skipline = 1; + print_lock("*TOTAL*", &sum_count, 0, entry_type); + skipline = 1; + } + + if (!directory[si].multilock) { + k = i; + reset_counts (&sum_count); + while (k < next_free_dir_index && strcmp(directory[sorti[k]].lock_name,directory[si].lock_name) == 0) { + add_counts (&sum_count, &total_counts[sorti[k]]); + k++; + } + skipline = 1; + if (sum_counts (&sum_count, entry_type)) { + print_lock(directory[si].lock_name, &sum_count, 0, entry_type); + for (j = i; j < k; j++) { + sj = sorti[j]; + set_counts (&sum_count, &total_counts[sj], entry_type); + if (sum_count.total) + print_lock(directory[sj].caller_name, &sum_count, 1, entry_type); + } + } + i = k - 1; + skipline = 1; + } else { + if (set_counts (&sum_count, &total_counts[si], entry_type)) + print_lock(directory[si].caller_name, &sum_count, 0, entry_type); + } + } +} + + + +void +reset_counts(lock_summary_t *sump) +{ + int j; + sump->counts.cum_hold_ticks = 0; + sump->counts.cum_wait_ticks = 0; + sump->counts.max_wait_ticks = 0; + sump->counts.max_hold_ticks = 0; + for (j = 0; j < LSTAT_ACT_MAX_VALUES; j++) + sump->counts.count[j] = 0; +} + + +int +set_counts(lock_summary_t *sump, lstat_lock_counts_t *countp, entry_type_enum entry_type) +{ + int j; + + sump->counts.cum_hold_ticks = countp->cum_hold_ticks; + sump->counts.cum_wait_ticks = countp->cum_wait_ticks; + sump->counts.max_wait_ticks = countp->max_wait_ticks; + sump->counts.max_hold_ticks = countp->max_hold_ticks; + for (j = 0; j < LSTAT_ACT_MAX_VALUES; j++) + sump->counts.count[j] = countp->count[j]; + return(sum_counts(sump, entry_type)); +} + + +void +add_counts(lock_summary_t *sump, lstat_lock_counts_t *countp) +{ + int j; + + sump->counts.cum_hold_ticks += countp->cum_hold_ticks; + sump->counts.cum_wait_ticks += countp->cum_wait_ticks; + if (sump->counts.max_wait_ticks < countp->max_wait_ticks) + sump->counts.max_wait_ticks = countp->max_wait_ticks; + if (sump->counts.max_hold_ticks < countp->max_hold_ticks) + sump->counts.max_hold_ticks = countp->max_hold_ticks; + for (j = 0; j < LSTAT_ACT_MAX_VALUES; j++) + sump->counts.count[j] += countp->count[j]; +} + + +int +sum_counts(lock_summary_t *sump, entry_type_enum entry_type) +{ + int total, j; + double contention; + + for (total = 0, j = 0; j < LSTAT_ACT_MAX_VALUES; j++) + total += sump->counts.count[j]; + sump->total = total; + sump->persec = total / deltatime; + + if (!total || entry_type == Special_Entry) + contention = 0.0; + else if (sump->counts.count[LSTAT_ACT_NO_WAIT] == 0) + contention = 100.0; + else + contention = 100.0 - (100.0 * sump->counts.count[LSTAT_ACT_NO_WAIT] / total); + + sump->contention = contention; + return (total && sump->persec > opt_persec && + (contention > opt_contention || entry_type == Sema_Entry)); +} + + +void +set_header(char *header) +{ + if (header != last_header) { + current_header = header; + last_header = header; + } +} + + +void +print_header(void) +{ + if (current_header != NULL) { + fprintf (stderr, "\n%s", dashes); + fprintf (stderr, "%s\n", current_header); + current_header = NULL; + } +} + + +void +print_title(char *title1, char *title2) +{ + struct utsname uts; + fprintf (stderr, "___________________________________________________________________________________________\n"); + uname (&uts); + fprintf (stderr, "System: %s %s %s %s %s\n", uts.sysname, uts.nodename, uts.release, uts.version, uts.machine); + if (ident) + fprintf(stderr, "Ident: %s\n", ident); + fprintf(stderr, "%s\n", title1); + fprintf(stderr, "%s\n", title2); + if (opt_persec >= 0 || opt_contention >= 0) { + fprintf(stderr, "Selecting locks: "); + if (opt_persec >= 0) + fprintf(stderr, "threshhold: >%d/sec ", (int)opt_persec); + if (opt_contention >= 0) + fprintf(stderr, "contention: >%d%%", (int)opt_contention); + fprintf(stderr, "\n"); + } + fprintf(stderr, "\n"); + fprintf(stderr, "Start time: %s", ctime(&start_time)); + fprintf(stderr, "End time: %s", ctime(&end_time)); + fprintf(stderr, "Delta Time: %.2f sec, slots in use: %d, \n", deltatime, next_free_dir_index); + +} + + +void +print_lock(char *name, lock_summary_t *sump, int indent, entry_type_enum entry_type) +{ + float hold_time, max_hold_time, wait_time, max_wait_time; + + print_header(); + + if (skipline) + fprintf(stderr, "\n"); + skipline = 0; + + fprintf(stderr, "%9.2f", sump->persec); + fprintf(stderr, "%4.0f%%", sump->contention); + if (entry_type == Mr_Entry || entry_type == Normal_Entry || entry_type == RWspin_Entry) { + hold_time = sump->counts.cum_hold_ticks / cycles_per_usec / sump->total; + max_hold_time = sump->counts.max_hold_ticks / cycles_per_usec; + wait_time = sump->counts.cum_wait_ticks / cycles_per_usec; + max_wait_time = sump->counts.max_wait_ticks / cycles_per_usec; + if (wait_time > 0) { + if (sump->counts.count[LSTAT_ACT_SLEPT]) { + wait_time /= sump->counts.count[LSTAT_ACT_SLEPT]; + } else if (sump->counts.count[LSTAT_ACT_SPIN]) { + wait_time /= sump->counts.count[LSTAT_ACT_SPIN]; + } else { + wait_time = 0; + } + } + + if (entry_type == Normal_Entry) { + if (hold_time > 0) { + fprintf(stderr, (hold_time < 10) ? "%6.1fus" : "%6.0fus", hold_time); + + if (max_hold_time < 10) + fprintf(stderr,"(%6.1fus)",max_hold_time); + else if (max_hold_time < 1000000) + fprintf(stderr,"(%6.0fus)",max_hold_time); + else + fprintf(stderr,"(%4.0fmsec)",max_hold_time/1000); + } else { /* no hold_time data, so just print blanks */ + fprintf(stderr," "); + } + } + + if (wait_time > 0) { + fprintf(stderr, (wait_time < 10) ? "%6.1fus" : "%6.0fus", wait_time); + + if (max_wait_time < 10) + fprintf(stderr,"(%6.1fus)",max_wait_time); + else if (max_wait_time < 1000000) + fprintf(stderr,"(%6.0fus)",max_wait_time); + else + fprintf(stderr,"(%4.0fmsec)",max_wait_time/1000); + } else { /* wait_time is zero */ + fprintf(stderr," 0us "); + } + } + fprintf(stderr, "%12d", sump->total); + fprintf(stderr, "%12d", sump->counts.count[LSTAT_ACT_NO_WAIT]); + fprintf(stderr, "%9d", sump->counts.count[LSTAT_ACT_SPIN]); +#ifdef notyet + fprintf(stderr, "%9d", sump->counts.count[LSTAT_ACT_SLEPT]); +#endif + fprintf(stderr, "%9d", sump->counts.count[LSTAT_ACT_REJECT]); + fprintf(stderr, "%s %s", (indent ? " ":""), name); + fprintf(stderr, "\n"); +} + + +void +get_collection_state(int verify_unix) +{ + lstat_user_request_t request; + int ret_count; + + ret_count = read(dataFile, (char *)&request, sizeof(request)); + if (ret_count != sizeof(request)) { + perrorx ("Lockstat version does not match kernel!"); + exit(1); + } + + if (request.lstat_version != LSTAT_VERSION) { + perrorx ("Lockstat version does not match kernel!"); + exit(1); + } + +#ifdef XXX_NOTYET + /* XXX lseek size was computed by kernel before cpucount known */ + if (((int)(general_read_buf_len = lseek(dataFile, 0, SEEK_END)) < 0) + || (lseek(dataFile, 0, SEEK_SET) < 0)) { + printf("...lseek error...\n"); + } +#endif + general_read_buf_len = + (sizeof(lstat_user_request_t)) + + (request.maxcpus * sizeof(lstat_cpu_counts_t)) + + (LSTAT_MAX_STAT_INDEX * sizeof(lstat_directory_entry_t)); + general_read_buf = (char *)malloc(general_read_buf_len); + if (!general_read_buf) { + perrorx ("Unable to malloc general_read_buf"); + exit(1); + } + + numcpus = request.maxcpus; + enabled = request.lstat_is_enabled; + cycles_per_usec = (float)request.cycleval / 1000000.0; + kernel_magic_addr = request.kernel_magic_addr; + kernel_end_addr = request.kernel_end_addr; +} + + + +void +enable_statistic_collection(int on_off) +{ + if (enabled && on_off) + notex("Lock statistics collection is already ON"); + else if (! enabled && !on_off) + notex("Lock statistics collection is already OFF"); + + general_read_buf[0] = on_off; + if (write(dataFile, general_read_buf, 1) <= 0) { + perrorx("Unexpected error turning statistics on or off"); + exit(1); + } +} + + +void +get_kernel_data(get_data_buffer_enum bufid) +{ + lstat_user_request_t request; + static int start_jiffies, end_jiffies; + int ret_count; + int cpu_counts_len; + int directory_len; + + if (debugopt == 2) + return; + + ret_count = read(dataFile, general_read_buf, general_read_buf_len); + if (ret_count != general_read_buf_len) { + perrorx("Unexpected error reading data"); + exit(1); + } + memcpy((void *)&request, general_read_buf, sizeof(lstat_user_request_t)); + + cpu_counts_len = numcpus * sizeof(lstat_cpu_counts_t); + directory_len = LSTAT_MAX_STAT_INDEX * sizeof(lstat_directory_entry_t); + + if (bufid == Buf_Previous) { + if (!prev_counts) + prev_counts = (lstat_cpu_counts_t *) + malloc(numcpus*sizeof(lstat_cpu_counts_t)); + request.cpu_counts_ptr = prev_counts; + request.directory_ptr = prev_directory; + } else { + if (!kernel_counts) + kernel_counts = (lstat_cpu_counts_t *) + malloc(numcpus*sizeof(lstat_cpu_counts_t)); + request.cpu_counts_ptr = kernel_counts; + request.directory_ptr = kernel_directory; + } + + memcpy((void *)request.cpu_counts_ptr, + general_read_buf + sizeof(lstat_user_request_t), + cpu_counts_len); + memcpy((void *)request.directory_ptr, + general_read_buf + sizeof(lstat_user_request_t) + + cpu_counts_len, + directory_len); + + if (bufid == Buf_Previous) { + start_time = request.current_time; + start_jiffies = request.current_jiffies; + } else { + if (topt) { + start_jiffies = request.enabled_jiffies; + start_time = request.current_time - + (request.current_jiffies-request.enabled_jiffies)/HZ; + } + end_time = request.current_time; + end_jiffies = request.current_jiffies; + deltatime = (double) (end_jiffies - start_jiffies) / 100.0; + } + + next_free_dir_index = request.next_free_dir_index; + if (debugopt && bufid == Buf_Current) + write_diff_file(); +} + + +void +build_sort_directory(void) +{ + static int last_next_free_dir_index = 1; + int i, lowbits; + char *namep; + +#ifdef ZZZ + { + int chain[64]; + int i, j, k, n; + for (i=0; i<64; i++) + chain[i] = 0; + for (i = 0; i < next_free_dir_index; i++) { + for (j=kernel_directory[i].next_stat_index, n=0; j; j = kernel_directory[j].next_stat_index) + n++; + chain[n]++; + } + printf ("Total entries %d\n", next_free_dir_index); + for (i=0; i<64; i++) + printf("Chain %3d, %5d\n", i, chain[i]); + exit(0); + } +#endif + for (i = last_next_free_dir_index; i < next_free_dir_index; i++) { + sorti[i] = i; + lowbits = ((unsigned long)kernel_directory[i].caller_ra & 3UL); + directory[i].caller_ra = (void *)((unsigned long)kernel_directory[i].caller_ra & ~3UL); + directory[i].caller_name = strspace(); + myaddress_to_symbol((void*)directory[i].caller_ra, directory[i].caller_name); + namep = strchr(directory[i].caller_name, '+'); + if (namep) + directory[i].caller_name_len = namep - directory[i].caller_name; + else + directory[i].caller_name_len = strlen(directory[i].caller_name); + + directory[i].lock_name = NULL; + directory[i].multilock = 0; + if (kernel_directory[i].lock_ptr != LSTAT_MULTI_LOCK_ADDRESS) { + namep = strspace(); /* ZZZ */ + myaddress_to_symbol(kernel_directory[i].lock_ptr, namep); + if (!isdigit(*namep)) + directory[i].lock_name = namep; + else + *namep = '\0'; + } + switch (lowbits) { + case LSTAT_RA_RWSPIN: + directory[i].entry_type = RWspin_Entry; + directory[i].title_index = 1; + break; + case LSTAT_RA_SEMA: + directory[i].entry_type = Sema_Entry; + directory[i].title_index = 2; + break; +#ifdef notyet + case LSTAT_RA_MR: + directory[i].entry_type = Mr_Entry; + directory[i].title_index = 3; + break; +#endif + default: + directory[i].entry_type = Normal_Entry; + directory[i].title_index = 0; + } + if (directory[i].lock_name == NULL) { + directory[i].lock_name = directory[i].caller_name; + directory[i].multilock = 1; + } + } + last_next_free_dir_index = next_free_dir_index; + + qsort ((void *) &sorti[1], next_free_dir_index - 1, sizeof(sorti[0]), &sortcmp); + +} + + +int +sortcmp(const void *ip, const void *jp) +{ + int si0, si1, k; + + si0 = *(short *)ip; + si1 = *(short *)jp; + + k = directory[si0].entry_type - directory[si1].entry_type; + if (k) + return (k); + + + k = directory[si0].multilock - directory[si1].multilock; + if (k) + return (k); + + k = strcmp(directory[si0].lock_name, directory[si1].lock_name); + if (k) + return (k); + + k = strncmp(directory[si0].caller_name, directory[si1].caller_name, + min(directory[si0].caller_name_len, + directory[si1].caller_name_len)); + if (k) + return (k); + + k = directory[si0].caller_ra - directory[si1].caller_ra; + return(k); + +} + + +void +sum_data (int start_cpu, int end_cpu) +{ + int i, j, cpu; + for (i = 0; i < next_free_dir_index; i++) { + total_counts[i].cum_hold_ticks = 0; + total_counts[i].cum_wait_ticks = 0; + total_counts[i].max_hold_ticks = 0; + total_counts[i].max_wait_ticks = 0; + for (j = 0; j < LSTAT_ACT_MAX_VALUES; j++) + total_counts[i].count[j] = 0; + for (cpu = start_cpu; cpu <= end_cpu; cpu++) { + total_counts[i].cum_hold_ticks += kernel_counts[cpu][i].cum_hold_ticks - + (topt ? 0 : prev_counts[cpu][i].cum_hold_ticks); + total_counts[i].cum_wait_ticks += kernel_counts[cpu][i].cum_wait_ticks - + (topt ? 0 : prev_counts[cpu][i].cum_wait_ticks); + if (total_counts[i].max_hold_ticks < kernel_counts[cpu][i].max_hold_ticks) + total_counts[i].max_hold_ticks = kernel_counts[cpu][i].max_hold_ticks; + if (total_counts[i].max_wait_ticks < kernel_counts[cpu][i].max_wait_ticks) + total_counts[i].max_wait_ticks = kernel_counts[cpu][i].max_wait_ticks; + for (j = 0; j < LSTAT_ACT_MAX_VALUES; j++) + total_counts[i].count[j] += kernel_counts[cpu][i].count[j] - + (topt ? 0 : prev_counts[cpu][i].count[j]); + } + } + +} + + +void +myaddress_to_symbol(void *adr, char *namep) +{ + /* + * Read the entire map file once and build an in-memory table of the + * address-name equivalences for quick reference. + */ + typedef struct { + unsigned long addr; + char *name; + } namespace_entry_t; +#define NAMESPACE_TABLE_CHUNK 2048 + static int namespaceTblMaxEntries = 0; + static int namespaceTblCurEntries = 0; + static namespace_entry_t *namespaceTbl = NULL; +#define MAX_MAPFILE_REC_LEN 128 + char buffer[MAX_MAPFILE_REC_LEN]; + unsigned long func_addr = 0; + char func_name[MAX_MAPFILE_REC_LEN]; + char mode[8]; + int i; + + /* Build the in-memory Namespace Table, if it doesn't already exist */ + if (!namespaceTbl) { + rewind(mapFile); + while (fgets(buffer, MAX_MAPFILE_REC_LEN, mapFile)) { + if (sscanf(buffer, "%lx %s %s", + &func_addr, mode, func_name) != 3) { + fatalx("Corrupted mapfile"); + exit(1); + } + if (namespaceTblCurEntries == namespaceTblMaxEntries) { + namespaceTblMaxEntries += NAMESPACE_TABLE_CHUNK; + namespaceTbl = (namespace_entry_t *)realloc((void *)namespaceTbl,(namespaceTblMaxEntries * sizeof(namespace_entry_t))); + if (!namespaceTbl) { + fatalx("Cannot create in-memory namespace table"); + exit(1); + } + } + namespaceTbl[namespaceTblCurEntries].addr = func_addr; + namespaceTbl[namespaceTblCurEntries].name = strspace(); + strcpy(namespaceTbl[namespaceTblCurEntries].name, func_name); + + /* monotonically increasing addresses? */ + if (namespaceTblCurEntries) + if (namespaceTbl[namespaceTblCurEntries-1].addr > func_addr) { + fatalx("map file must has ascending addresses"); + exit(1); + } + + ++namespaceTblCurEntries; + } + } + + if (opt_debug) + namep += sprintf(namep, "[0x%lx] ", (unsigned long)adr); + else + namep[0] = '\0'; /* ensure that subsequent strcat() will work */ + + for (i = 0; i < namespaceTblCurEntries; i++) { + if ((unsigned long)adr == namespaceTbl[i].addr) { + strcat(namep, namespaceTbl[i].name); + return; + } else if ((unsigned long)adr < namespaceTbl[i].addr) { + if (i == 0) + break; /* below the first address in the table */ + sprintf(namep, "%s+0x%x", namespaceTbl[i-1].name, + (unsigned long)adr - namespaceTbl[i-1].addr); + return; + } + } + + /* can't find it */ + if (!opt_debug) + sprintf(namep, "[0x%lx]", (unsigned long)adr); +} + + +/* + * strspace + * + * A crude allocation manager for string space. + * Returns pointer to where a string of length 0 .. STRMAX can be placed. + * On next call, a free space pointer is updated to point to + * the location just beyond the last string allocated. + * NOTE: does not support expanding a string once allocated & + * another call to strspace is made. + */ +char* +strspace(void) +{ + static char *space=NULL, *endspace=NULL; + + if (space) + space += (1 + strlen(space)); + + if (space >= endspace) { + space = (char *)malloc(SPACEINC); + endspace = space + SPACEINC - STRMAX; + } + return(space); +} + + + +void +verify_diff_file (void) +{ + int i; + + for (i = 0; i < next_free_dir_index; i++) { + if (kernel_directory[i].caller_ra != prev_directory[i].caller_ra && prev_directory[i].caller_ra != 0) { + fprintf(stderr, "caller address mismatch: index:%d, old:%llx, new:%llx", + i, prev_directory[i].caller_ra, kernel_directory[i].caller_ra); + perrorx("caller address mismatch"); + } + } +} + +#define WRITE_STR(s) if (write(fd, (char *)&s, sizeof(s)) != sizeof(s)) \ + perrorx("write diff stats"); +#define READ_STR(s) if (read(fd, (char *)&s, sizeof(s)) != sizeof(s)) \ + perrorx("read diff stats s "); +#define WRITEN(s,n) if (write(fd, (char *)&s, (n)) != (n)) \ + perrorx("write diff stats"); +#define READN(s,n) if (read(fd, (char *)&s, (n)) != (n)) \ + perrorx("read diff stats"); +int +read_diff_file (void) +{ + int fd; + + if ((fd = open(debugname, O_RDONLY, 0)) < 0) + return (0); + + READ_STR(numcpus); + prev_counts = (lstat_cpu_counts_t *) + malloc(numcpus*sizeof(lstat_cpu_counts_t)); + kernel_counts = (lstat_cpu_counts_t *) + malloc(numcpus*sizeof(lstat_cpu_counts_t)); + READ_STR(start_time); + READ_STR(end_time); + READ_STR(deltatime); + READ_STR(next_free_dir_index); + READN(kernel_directory[0], next_free_dir_index*sizeof(lstat_directory_entry_t)); + READN(kernel_counts[0], numcpus*sizeof(lstat_cpu_counts_t)); + READN(prev_counts[0], numcpus*sizeof(lstat_cpu_counts_t)); + READ_STR(kernel_magic_addr); + READ_STR(kernel_end_addr); + + close(fd); + verify_diff_file (); + enabled = LSTAT_ON; + return(1); +} + + +void +write_diff_file (void) +{ + int fd; + + if ((fd = open(debugname, O_WRONLY | O_CREAT, 0666)) < 0) + perrorx("cant create diff file"); + + WRITE_STR(numcpus); + WRITE_STR(start_time); + WRITE_STR(end_time); + WRITE_STR(deltatime); + WRITE_STR(next_free_dir_index); + WRITEN(kernel_directory[0], next_free_dir_index*sizeof(lstat_directory_entry_t)); + WRITEN(kernel_counts[0], numcpus*sizeof(lstat_cpu_counts_t)); + WRITEN(prev_counts[0], numcpus*sizeof(lstat_cpu_counts_t)); + WRITE_STR(kernel_magic_addr); + WRITE_STR(kernel_end_addr); + + close(fd); + exit(0); +} + diff -urN 2.4.0/scripts/memleak/FAQ 2.4.0-ikd-1/scripts/memleak/FAQ --- 2.4.0/scripts/memleak/FAQ Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/scripts/memleak/FAQ Mon Jan 15 18:40:05 2001 @@ -0,0 +1,83 @@ + + + how to find memory leaks + +first, since the kernel is written in C, memory leaks are hard +to find. Most if not all data given by this patch/tools are +heuristic, use common sense and testing before calling something a +'memory leak'. + +terms: + + - 'allocation point': a certain point in the kernel source where + a memory object is allocated via some allocator, eg. kmalloc() + or get_free_pages(). + + - 'allocation count': the number of not-yet-freed allocations + done at a certain allocation point. + + - 'memory leak': a 'forgotten' buffer, too many such buffers + might cause system slowdown/hangups. + + +install the patch and reboot into the new kernel. You should +see the /proc/memleak file, with such contents: + +pc7537:~> head /proc/memleak +<00000000> jiffies.c:0 (8179) +<00001000> vmalloc.c:124 (0) +<00002000> filemap.c:274 (7946) +<00003000> vmalloc.c:124 (0) +<00004000> fork.c:237 (0) +<00005000> fork.c:186 (0) +<00005800> fork.c:186 (0) +<00006000> fork.c:237 (0) +<00007000> vmalloc.c:124 (0) +<00008000> buffer.c:1349 (8156) + +The first entry is a 'special' entry. + +units are seconds since bootup when it was allocated. The 'jiffies' line +shows the current 'elapsed seconds' value. + +eg.: + +<00002000> memory.c:930 (4838) <---------------------- was allocated 4838 + seconds after boot. + ^---------------------- was allocated here + ^------- object address + + +the special entry 'jiffies.c' shows elapsed time since bootup: + +<00000000> jiffies.c:0 (5269) <---------- 5269 seconds elapsed since this + system booted. + + + +the second thing you might want to try is the 'dosum' script: + +774 buffer.c:1349 +9 console.c:322 +9 console.c:325 + +the first number is the 'allocation count', the number of memory objects +allocated in a certain FILE:LINE. If some allocation point shows a constantly +increasing allocation count, it's probably a memory leak. + +NOTE: the VM subsystems usually have very fluctuating allocation counts, +think twice before calling them a memory leak. + +piping /proc/memleak through the 'cutvm' script filters these allocations +out. + +There are other scripts too, read the comments in them to find out what +they do ... and you might want to write custom scripts yourself, if you +have a specific thing to debug. The memcheck.sh script stores the current +allocation map into a RCS tree. RCS is storing 'delta' maps very +effectively. Use "rcsdiff -r{} -r{}" to see the delta quickly. + +thats all for now, + + Ingo + diff -urN 2.4.0/scripts/memleak/Makefile 2.4.0-ikd-1/scripts/memleak/Makefile --- 2.4.0/scripts/memleak/Makefile Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/scripts/memleak/Makefile Mon Jan 15 18:40:05 2001 @@ -0,0 +1,9 @@ +all: findaddr + +TMPCFLAGS=$(CFLAGS:%-pg=%) +# True userspace program, remove the __KERNEL__ flag +findaddr: findaddr.c + $(CC) $(TMPCFLAGS:%-g=%) -U__KERNEL__ -o findaddr findaddr.c + +clean: + rm -f findaddr diff -urN 2.4.0/scripts/memleak/doalloc 2.4.0-ikd-1/scripts/memleak/doalloc --- 2.4.0/scripts/memleak/doalloc Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/scripts/memleak/doalloc Mon Jan 15 18:40:05 2001 @@ -0,0 +1,11 @@ +#!/bin/bash + +# +# this script post-processes files generated by 'dorun'. +# it lists allocation points and multiple allocation counts in one line +# one line shows how a particular allocation point 'evolves' in time +# + + +for N in `cut -f2 9*| sort| uniq`; do echo $N: `grep $N 9*| cut -d: -f4| cut -f1`; done + diff -urN 2.4.0/scripts/memleak/docutvm 2.4.0-ikd-1/scripts/memleak/docutvm --- 2.4.0/scripts/memleak/docutvm Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/scripts/memleak/docutvm Mon Jan 15 18:40:05 2001 @@ -0,0 +1,9 @@ +#!/bin/bash + +# +# this script eliminates 'VM/buffer cache' allocations, these +# are harder to understand and their allocation count fluctuates wildly. +# + +grep -v slab.c | grep -v memory.c | grep -v swap_state.c | grep -v filemap.c | grep -v file_table.c | grep -v buffer.c | grep -v dcache.c | grep -v pgtable | grep -v mmap.c | grep -v fork.c | grep -v exec.c + diff -urN 2.4.0/scripts/memleak/dodelta 2.4.0-ikd-1/scripts/memleak/dodelta --- 2.4.0/scripts/memleak/dodelta Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/scripts/memleak/dodelta Mon Jan 15 18:40:05 2001 @@ -0,0 +1,9 @@ +#!/bin/bash + +# +# same as 'doalloc', but it lists delta allocations, not full number +# of allocations +# + +for N in `cut -f2 0*| sort| uniq`; do ( P=0;F=1;for M in `grep $N 0*| cut -d: -f4| cut -f1`; do if [ "$F" = 1 ]; then F=0; FIRST=$M; fi; echo $[$M-$P]; P=$M; done; echo "DELTA: $[$M-$FIRST]";) | xargs echo $N: ; done + diff -urN 2.4.0/scripts/memleak/dofind 2.4.0-ikd-1/scripts/memleak/dofind --- 2.4.0/scripts/memleak/dofind Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/scripts/memleak/dofind Mon Jan 15 18:40:05 2001 @@ -0,0 +1,14 @@ +#!/bin/bash + +# +# this script lists wether all 'currently allocated' memory +# objects are actually referenced to in the kernel ... this +# isnt a 100% sure method, but usually a 'used' object has it's address +# listed somewhere, while a 'leaked' object doesnt have any +# references anymore. +# + +cp /proc/memleak /tmp/leak3 + +for N in `cat /tmp/leak3 | cut -c2-9`; do findaddr 0x$N; done + diff -urN 2.4.0/scripts/memleak/dorun 2.4.0-ikd-1/scripts/memleak/dorun --- 2.4.0/scripts/memleak/dorun Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/scripts/memleak/dorun Mon Jan 15 18:40:05 2001 @@ -0,0 +1,14 @@ +#!/bin/bash + +# +# this script puts an 'allocation summary' into the current +# directory every 10 seconds. +# +# you can analyze these files via 'doalloc' and 'dodelta', +# to find suspicious allocation points. +# + +while true; do + FILE=`date +'%y-%m-%d__%T'`; sync; sleep 10; echo $FILE; dosum > $FILE; +done + diff -urN 2.4.0/scripts/memleak/dosum 2.4.0-ikd-1/scripts/memleak/dosum --- 2.4.0/scripts/memleak/dosum Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/scripts/memleak/dosum Mon Jan 15 18:40:05 2001 @@ -0,0 +1,8 @@ +#!/bin/bash + +# +# generates 'current allocation summary' +# + +(cat /proc/memleak | cut -d'(' -f1 > /tmp/leak; cat /tmp/leak )| cut -c12- | sort | gawk -- 'BEGIN{Y=0;}//{if ($0 == X) {Y=Y+1;} else {if (Y) printf ("%d\t %s\n", Y, X); Y=1;} X=$0}END{ printf ("%d\t %s\n", Y, $0);}' + diff -urN 2.4.0/scripts/memleak/dotimestamp 2.4.0-ikd-1/scripts/memleak/dotimestamp --- 2.4.0/scripts/memleak/dotimestamp Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/scripts/memleak/dotimestamp Mon Jan 15 18:40:05 2001 @@ -0,0 +1,10 @@ +#!/bin/bash + +# +# this script generates a timestamp-sorted list of allocations. +# +# 'old' (low timestamp value) allocations have a higher chance +# that they are actually leaked away objects. +# + +cp /proc/memleak /tmp/leak2; sort -n -t'(' +1 /tmp/leak2 diff -urN 2.4.0/scripts/memleak/findaddr.c 2.4.0-ikd-1/scripts/memleak/findaddr.c --- 2.4.0/scripts/memleak/findaddr.c Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/scripts/memleak/findaddr.c Mon Jan 15 18:40:05 2001 @@ -0,0 +1,68 @@ + +/* + * find a pointer in /proc/kcore (all system memory) + * + * a leaked object probably hasnt got any references in + * memory. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define N 4096 + +char buffer [N]; + +char * fool_libc = "0x0deadbee"; + +int main(int argc, char * * argv) +{ + int file = open("/proc/kcore",O_RDONLY); + int n,i,hits=0; + unsigned int addr, pos=0, fool_addr; + + if (argc != 2) { + fprintf(stderr,"usage: findaddr 0x\n"); + exit(-1); + } + if (file==-1) { + perror("couldn't open /proc/kcore\n"); + exit(-1); + } + sscanf(argv[1],"0x%08x",&addr); + + addr--; + + sscanf(fool_libc,"0x%08x",&fool_addr); + + while ((n = read(file,buffer,N)) > 0) { + for (i=0; i<=n-sizeof(int); i++) { + + if ((*((int *)&(buffer[i])))-1 == addr) { + if (++hits) { + printf("found 0x%08x at %08x\n", addr+1, pos+i*sizeof(int)); + goto out; + } + } + + } + pos += n; + } + if (!n) + printf("0x%08x not found!\n", addr+1); +out: + return (0); +} + + diff -urN 2.4.0/scripts/memleak/memcheck.sh 2.4.0-ikd-1/scripts/memleak/memcheck.sh --- 2.4.0/scripts/memleak/memcheck.sh Thu Jan 1 01:00:00 1970 +++ 2.4.0-ikd-1/scripts/memleak/memcheck.sh Mon Jan 15 18:40:05 2001 @@ -0,0 +1,7 @@ +#!/bin/bash +TEMPFILE=`tempfile` +LOGFILE=/var/log/memleak + cat /proc/memleak | cut -c12- | sort | gawk -- 'BEGIN{Y=0;}//{if ($0 == X) {Y=Y+1;} else {if (Y) printf ("%d\t %s\n", Y, X); Y=1;} X=$0}END{printf ("%d\t %s\n", Y, $0);}' > $TEMPFILE +co -l $LOGFILE &>/dev/null +mv $TEMPFILE $LOGFILE +echo "." | ci $LOGFILE &>/dev/null