diff -urN ikd-ref/Documentation/00-INDEX ikd/Documentation/00-INDEX --- ikd-ref/Documentation/00-INDEX Mon Jan 17 16:44:32 2000 +++ ikd/Documentation/00-INDEX Tue Oct 3 20:08:45 2000 @@ -33,6 +33,8 @@ - directory with information on the CD-ROM drivers that Linux has. cpqarray.txt - info on using Compaq's SMART2 Intelligent Disk Array Controllers. +debugging.txt + - summary of the integrated kernel debugging patch devices.tex - TeX source listing of all the nodes in /dev/ with major minor #'s devices.txt @@ -77,6 +79,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 ikd-ref/Documentation/Configure.help ikd/Documentation/Configure.help --- ikd-ref/Documentation/Configure.help Tue Oct 3 20:08:37 2000 +++ ikd/Documentation/Configure.help Tue Oct 3 20:08:45 2000 @@ -11328,6 +11328,161 @@ This driver differs slightly from OSS/Free, so PLEASE READ the comments at the beginning of driver/sound/trident.c +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. + +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 + WARNING: This is a brute-force application and will most-likely + cause a kernel oops. Use this with extreme caution! + 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. + +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. + +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. + +SMP-NMI Software Watchdog +CONFIG_NMI_WATCHDOG + This software watchdog only works on Intel-SMP compliant boxes. The + driver tweaks the IRQ-delivery mechanizm to route a periodic NMI + interrupt to all CPUs. If one of the CPUs is detected as 'locked up' + in the nmi-handler, then an oops is generated. This oops can then be + used to debug the lockup. To determine if the NMI oopser is operating + properly, after booting the kernel, cat /proc/interrupts and verify + that you are getting NMIs. When using this feature, a serial console + makes recording of the output easier. + +SMP-NMI-Bug Workaround, Alternative Watchdog IRQ Source +CONFIG_NMI_WATCHDOG_IRQ + Some boards are buggy and cannot generate a periodic NMI interrupt. Use + non-zero IRQ as an NMI source in this case, eg. IRQ=1 (keyboard IRQ). + This has the drawback that in the case of a lockup you'll have to + generate some keyboard IRQs to see the oops. (type on the keyboard) + +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. + +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. To enable kdb, say Y here. + Rockwell WaveArtist CONFIG_SOUND_WAVEARTIST Say Y here to include support for the Rockwell WaveArtist sound @@ -11377,6 +11532,80 @@ by pressing various keys while holding SysRq (Alt+PrintScreen). The keys are documented in Documentation/sysrq.txt. Don't say Y unless you really know what this hack does. + +Kernel messages dumper +CONFIG_KMSGDUMP + If you say Y here, you will get maximal chances to save your kernel + messages under weird conditions : you'll be able to display them on + the screen, print them on a parallel printer, and dump them onto a + floppy diskette. You will have to press SysRQ + D to access this + feature, or wait for a kernel panic. In the last case, it is also + possible to ask the kernel not to wait for any human operation, and + automatically dump its messages onto a diskette (see the option + CONFIG_KMSGDUMP_AUTO below). + + Warning: when using this feature, the CPU is rebooted in real mode + and the kernel won't recover. If messages are dumped onto a floppy, + this floppy will be erased, unless you enable the option + CONFIG_KMSGDUMP_SAFE below. More info can be found in the file + Documentation/kmsgdump.txt. + + All options below are used for default behaviour, but can be set + by writing to /proc/sys/kernel/kmsgdump if CONFIG_SYSCTL is enabled. + + Don't say Y unless you really want to hack your kernel and/or help + developpers to debug it. This isn't a toy, you have been warned ! + +Format floppy as FAT by default +CONFIG_KMSGDUMP_FAT + By default, when CONFIG_KMSGDUMP is enabled, all messages dumped on + a floppy are in raw format, beginning at the first sector of the + diskette. By enabling this option, you'll change the default format + to FAT12. Although this is selectable under interactive mode, this is + not the case after a kernel panic, where this option applies most. + + When FAT is selected, the messages are saved into a file named + MESSAGES.TXT. This practice is discouraged if you share your computer + with anyone else, since other people may believe that if FAT is + supported, the contents of the diskette would not be lost, which is + false. It's better if people remember that the diskette is unusable + after a dump. On the other hand, accessing the file after a crash is + easier when the diskette is formated as FAT. One other advantage of + FAT is that the boot sector of the diskette is filled with a boot + redirector which makes the system boot from the first hard disk even + if the bios tried to boot from the floppy. If unsure, say Y. + +Automatic dump then reboot on kernel panic +CONFIG_KMSGDUMP_AUTO + When the kernel panics, it normally hangs or reboots. With + CONFIG_KMSGDUMP enabled, it will either enter the interactive mode + you can see by pressing SysRQ + D (default), or, if you say Y here, + automatically dump its messages buffer onto a diskette and then reboot. + + Enabling this is dangerous because if a diskette is left in the + floppy drive, it may be destroyed (unless you enable the option + CONFIG_KMSGDUMP_SAFE below), but in case of a server, this might + be interesting since this one will automatically reboot after a + panic. Don't say Y here if this kernel is to be installed on a + floppy ! You would say Y in case of a server, N in case of a + workstation. + +Limit dump to pre-initialized floppies only +CONFIG_KMSGDUMP_SAFE + When CONFIG_KMSGDUMP_AUTO is enabled, the kernel can automatically + dump its messages onto the floppy present in the drive during a + crash. This can be dangerous because if a floppy has been forgotten + in the drive it may be overwritten. By enabling this option, the + kernel will accept to write to the floppy only if it finds the word + "KMSGDUMP" at offset 3 on the floppy. + + So to prepare a disk to accept dumps, you just have to do this : + + echo "012KMSGDUMP" > /dev/fd0 + + Please note that in case of interactive mode dump, this check is + never made because we consider that the user knows what stands on + his diskettes. If unsure, just say "Y". ISDN subsystem CONFIG_ISDN diff -urN ikd-ref/Documentation/debugging.txt ikd/Documentation/debugging.txt --- ikd-ref/Documentation/debugging.txt Thu Jan 1 01:00:00 1970 +++ ikd/Documentation/debugging.txt Tue Oct 3 20:08:45 2000 @@ -0,0 +1,78 @@ +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. + + NMI oopser + + Written by Ingo Molnar. + Integration into IKD and fixes for newer kernels + by Andrea Arcangeli. + + kdb + Written by Scott Lurndal (SGI) + Integration into IKD by Andrea Arcangeli. + + kmsgdump + + Written by Willy Tarreau + +The original merge of debugging tools into a single patch set (IKD) +is been done by Keith Owens . +PGP 917/C817FEC9. +Fingerprint 2B 25 0A 31 02 AE CA F7 73 0C 28 69 4A 7B 65 27 + +Currently the IKD patch is maintained by Andrea Arcangeli +and is dowloadable at: + + ftp://ftp.suse.com/pub/people/andrea/kernel-patches/ikd/ + +-------------------------------------------------------------------- +This patch adds assorted options underneath Kernel hacking. It also +adds commands OOPS, STACKFAULT and KERNEL_LOOP to the reboot syscall. +Note that these reboot commands are not controlled by config options, +applying the patch makes them available. Command OOPS lets root force +a kernel oops from user space, the other two commands are mainly used +for testing kernel recovery from stack overflow and never ending loops. +Trivial code to cause a kernel oops with this patch :- + +#include +#include +int main(void) +{ + return(reboot(0xfee1dead, 672274793, LINUX_REBOOT_CMD_OOPS)); +} diff -urN ikd-ref/Documentation/kdb/kdb.mm ikd/Documentation/kdb/kdb.mm --- ikd-ref/Documentation/kdb/kdb.mm Thu Jan 1 01:00:00 1970 +++ ikd/Documentation/kdb/kdb.mm Tue Oct 3 20:08:45 2000 @@ -0,0 +1,97 @@ +.ND "March 10, 1999" +.TL +Built-in Kernel Debugger for Linux +.AU "Scott Lurndal" SL 8U500 OS 33158 +.AT "Member Technical Staff" +.AF "Silicon Graphics, Inc." +.MT 2 +.AS +These programmer notes describe the built-in kernel debugger +for linux. +.AE +.H 1 "Overview" +This document describes the built-in kernel debugger available +for linux. This debugger allows the programmer to interactivly +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 which enables all +symbols with global scope (including static symbols) to be used +as arguments to the kernel debugger commands. +.H 1 "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_KDB_FRAMEPTR\fP +option be enabled. \fBCONFIG_KDB_FRAMEPTR\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 +When booting the new kernel using \fIlilo\fP(1), the 'kdb' 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. If the kdb flag isn't provided, +then kdb will automatically be invoked upon system panic or +when the +\fBPAUSE\fP +key is used from the keyboard. +.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 with input from the serial port and +output to the serial console. +.H 2 "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 +bpa Set or Display breakpoint globally +cpu Switch cpus +env Show environment +go Restart execution +help Display help message +id Disassemble Instructions +ll Follow Linked Lists +md Display memory contents +mds Display memory contents symbolically +mm Modify memory contents +reboot Reboot the machine +rd Display register contents +rm Modify register contents +set Add/change environment variable +.TE +.DE +.P +Further information on the above commands can be found in +the appropriate manual pages. 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. +.H 1 Writing new commands +.H 2 Writing a built-in command +.H 2 Writing a modular command diff -urN ikd-ref/Documentation/kdb/kdb_bp.man ikd/Documentation/kdb/kdb_bp.man --- ikd-ref/Documentation/kdb/kdb_bp.man Thu Jan 1 01:00:00 1970 +++ ikd/Documentation/kdb/kdb_bp.man Tue Oct 3 20:08:45 2000 @@ -0,0 +1,131 @@ +.TH BD 1 "09 March 1999" +.SH NAME +bc, bd, be, bl, bp, bpa \- breakpoint commands +.SH SYNOPSIS +bp \fIaddress-expression\fP [\f(CWDATAR|DATAW|IO\fP [\fIlength\fP]] +.LP +bpa \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 +The +.B bp +command is 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 +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 +Three 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. + +.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 +thus placing a limit of four on the number of data and I/O +breakpoints that may be established. + +.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 will 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. + +.TP 8 +IO +Enters the kernel debugger when an \fBin\fP or \fBout\fP instruction +targets the specified I/O address. + +.P +The +.B bpa +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 debuggers breakpoint table. This can be used to +keep more than 4 breakpoints in the breakpoint table without +exceeding the processor breakpoint register count. +.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 +Currently the kernel debugger does not use the int 03 method +of establishing instruction breakpoints, so there may only be +four active instruction and data breakpoints at any given time. +.P +There is a compile time limit of sixteen entries in the +breakpoint table at any one time. +.SH ENVIRONMENT +The breakpoint subsystem does not currently use any environment +variables. +.SH SMP CONSIDERATIONS +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 'bpa' command. +.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 +bp 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 +bp 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. + diff -urN ikd-ref/Documentation/kdb/kdb_bt.man ikd/Documentation/kdb/kdb_bt.man --- ikd-ref/Documentation/kdb/kdb_bt.man Thu Jan 1 01:00:00 1970 +++ ikd/Documentation/kdb/kdb_bt.man Tue Oct 3 20:08:45 2000 @@ -0,0 +1,67 @@ +.TH BT 1 "15 March 1999" +.SH NAME +bt \- Stack Traceback command +.SH SYNOPSIS +bt [ ] +.LP +btp +.SH DESCRIPTION +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_KDB_FRAMEPTR\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_KDB_FRAMEPTR\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). +.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 +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. +.SH SMP CONSIDERATIONS +None. +.SH EXAMPLES +.nf +.na +.ft CW +[root@host /root]# cat /proc/partitions +Entering kdb on processor 0 due to Debug Exception @ 0xc01845e3 +Read/Write breakpoint #1 at 0xc024ddf4 +kdb> bt + EBP Caller Function(args) +0xc74f5f44 0xc0146166 get_partition_list(0xc74d8000) +0xc74f5f8c 0xc01463f3 get_root_array(0xc74d8000, 0x13, 0xc74f5f88, 0xf3, 0xc00) +0xc74f5fbc 0xc0126138 array_read(0xc76cd80, 0x804aef8, 0xc00, 0xc76cdf94) +0xbffffcd4 0xc0108b30 sys_read(0x3, 0x804aef8, 0x1000, 0x1000, 0x804aef8) +kdb> bp +Instruction Breakpoint #0 at 0xc0111ab8 (schedule) in dr0 is disabled on cpu 0 +Data Access Breakpoint #1 at 0xc024ddf4 (gendisk_head) in dr1 is enabled on cpu 0 +for 4 bytes +kdb> go +[root@host /root]# diff -urN ikd-ref/Documentation/kdb/kdb_env.man ikd/Documentation/kdb/kdb_env.man --- ikd-ref/Documentation/kdb/kdb_env.man Thu Jan 1 01:00:00 1970 +++ ikd/Documentation/kdb/kdb_env.man Tue Oct 3 20:08:45 2000 @@ -0,0 +1,46 @@ +.TH ENV 1 "09 March 1999" +.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 FUTURE +Allow compile-time initialization of customized environment +settings. +.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 ikd-ref/Documentation/kdb/kdb_ll.man ikd/Documentation/kdb/kdb_ll.man --- ikd-ref/Documentation/kdb/kdb_ll.man Thu Jan 1 01:00:00 1970 +++ ikd/Documentation/kdb/kdb_ll.man Tue Oct 3 20:08:45 2000 @@ -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 ikd-ref/Documentation/kdb/kdb_md.man ikd/Documentation/kdb/kdb_md.man --- ikd-ref/Documentation/kdb/kdb_md.man Thu Jan 1 01:00:00 1970 +++ ikd/Documentation/kdb/kdb_md.man Tue Oct 3 20:08:45 2000 @@ -0,0 +1,86 @@ +.TH MD 1 "09 March 1999" +.SH NAME +md, mds, mm\- Memory manipulation commands +.SH SYNOPSIS +md [ \fIaddress-expression\fP [ \fIline-count\fP [\fIoutput-radix\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 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. +.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. +.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 ikd-ref/Documentation/kdb/kdb_rd.man ikd/Documentation/kdb/kdb_rd.man --- ikd-ref/Documentation/kdb/kdb_rd.man Thu Jan 1 01:00:00 1970 +++ ikd/Documentation/kdb/kdb_rd.man Tue Oct 3 20:08:45 2000 @@ -0,0 +1,54 @@ +.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 +.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. +.SH LIMITATIONS +Currently the 'rm' command will not allow modification of the +control or debug registers. +.P +Currently neither the 'rd' command nor the 'rm' 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. diff -urN ikd-ref/Documentation/kdb/kdb_ss.man ikd/Documentation/kdb/kdb_ss.man --- ikd-ref/Documentation/kdb/kdb_ss.man Thu Jan 1 01:00:00 1970 +++ ikd/Documentation/kdb/kdb_ss.man Tue Oct 3 20:08:45 2000 @@ -0,0 +1,71 @@ +.TH SS 1 "29 March 1999" +.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 will be released from the kernel debugger +when the instruction is traced, and will be brought back to +a barrier in the kernel debugger when the traced instruction +completes. +.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 ikd-ref/Documentation/kmsgdump.txt ikd/Documentation/kmsgdump.txt --- ikd-ref/Documentation/kmsgdump.txt Thu Jan 1 01:00:00 1970 +++ ikd/Documentation/kmsgdump.txt Tue Oct 3 20:08:45 2000 @@ -0,0 +1,362 @@ + - Documentation for KMSGDUMP v0.4 - + [Sun Sep 19 19:30:32 CEST 1999] - Willy Tarreau + + +1. What is KMSGDUMP ? +~~~~~~~~~~~~~~~~~~~~~ + +KMSGDUMP is an extension to the Linux kernel which allows the user on the +console to dump the last kernel messages onto a floppy diskette, thus +avoiding to take a pen and a paper to copy them when the system is stuck. +Only 3"1/2, 1.44 MB diskettes are supported by default. Other capacities +might work, provided you change the geometry in the file "kmsgdump.h". + + +2. How does it work ? +~~~~~~~~~~~~~~~~~~~~~ + +There are two ways of getting a dump : + + - by pressing SysRQ+D (RightAlt - PrintScrn - D together) ; + - after a kernel panic has occured, a dump may be automatically + generated. + +Before anything else, you MUST KNOW that in order to get maximal +chances to complete the dump succesfully, the CPU is rebooted in +real mode and disk accesses are made via the Bios. This ensures +that even if kernel memory is really corrupted, the dump still +has chances to work, but this also implies that after a dump has +occured, it is IMPOSSIBLE TO CONTINUE TO WORK WITH THE CURRENT +KERNEL. You will have to REBOOT. So when your kernel still responds, +you'd better get a similar dump by entering one of the following +commands : + +# dmesg > /dev/fd0 ( for RAW mode ) + +or + +# dmesg | mwrite a:messages.txt ( for FAT mode ) + + +Second, be sure that FLOPPY CONTENTS WILL BE LOST AFTER A DUMP. +Even if there are cases in which you can dump at the end of a diskette without +losing the beginning, consider that by default the beginning of the diskette +will be ERASED and you won't be able to recover what's on it. You have been +warned. + + +3. Modes of operation +~~~~~~~~~~~~~~~~~~~~~ + +There are two modes of operation : manual and automated. + +Manual mode (or interactive mode) is always entered if you hit SysRQ+D. +But it is also entered during a kernel panic if the current mode is set +to "manual". This mode is recommended for a developper's workstation, +or a kernel running under an emulator such as vmware. It's recommended +to disable interactive mode on servers which may crash when nobody is +near to reboot them. + +Automatic mode can only be entered during a kernel panic and if automatic +mode was previously configured. Sometimes, the system is really weird and +even kmsgdump can cause recursive crashes (this has been reported to me once). +For this reason I've added a checkpoint mechanism to the code : every little +part of code is checkpointed, and if a crash occurs again, the same part is +not executed again, to prevent loopings. So there are more chances to get +to the reset routine which will, in the worst case, reboot the system, but +not let it loop undefinetely. + +3.1. Manual mode +~~~~~~~~~~~~~~~~ + +Under manual mode, the screen initialized to color 80x25 mode (bios mode 3) +with a blue background. + + [Note: some people asked me to set other colors to avoid confusion + with another OS' BSOD, but I couldn't find good associations. + Eventhough I've received an interesting comment about the way + to choose colors readable on any color or monochrome display, + I'm waiting for suggestions, and for the moment we'll say that + these are the colors of Midnight Commander and call this "BSOL" + (blue screen of life) because this one is interactive.] + +The screen is divided in two portions. The upper one displays the current +status (kernel version, drive unit, printer, format...), and the lower one +the messages captured before switching to real mode. The internal speaker +beeps if a key has not been hit within 3 seconds. This is simply to get +someone's attention, mainly in cases where no monitor is connected to the +PC. + +The interface is not case-sensitive about keys pressed. Keys used are : + Upper arrow : scroll messages to the beginning + Lower arrow : scroll messages to the end + B : immediately reBoot the system + D : Dump messages onto the selected floppy with selected format. Warning: + no check is done before, and the floppy will simply be overwritten by + the messages. + F : select Format, by switching between RAW and FAT12 + H : immediately Halt the system. + I : display Information, little help about the keys. + P : Print messages on the currently selected printer. If you press this key + by accident, wait about one minute for the bios routine to timeout, and + you'll here the beeps again, stating that you can play again. + T : select next available prinTer. The system tests if a printer is + connected at the other end of the cable, and skips the empty ports. + U : change drive Unit. Although dump is possible on hard disks, they are + never proposed in the interface to avoid dramatical mistakes. + +Other keys are simply ignored. + +After a succesful dump or print, 3 quick beeps are played. In case of an error, +only one beep is played. This is important if you act blindly with a keyboard +and no monitor. + +3.2. Automatic mode +~~~~~~~~~~~~~~~~~~~ + +Automated operation is performed by the system only when a kernel panic +occurs. In this case, the system waits for the "panic_timeout" delay +to let you a few seconds if you want to try to play with SysRQ (sync, +unmount filesystems, ...). This delay is configurable by entering a +number of seconds in "/proc/sys/kernel/panic". + +After that, the system is rebooted to real mode, and depending on the +mode of operation chosen, either the interactive mode is entered (see above) +or it is the automatic mode, which we'll describe here. + +3.2.1. Start of operation +~~~~~~~~~~~~~~~~~~~~~~~~~ +Some checks are performed. First, the system sees if the dump feature is +enabled or not. If not, operation ends (see below). If dump is enabled, +and if the "safe" flag is enabled, the diskette is verified to be a real +"KMSGDUMP" diskette and not another one (read section 4 to know how to +prepare a secure diskette for KMSGDUMP). If the diskette isn't a right one, +operation ends. If the diskette is a right one, or if the check has been +disabled, the dump is performed with the current parameters (unit, format...). + +3.2.2. End of operation +~~~~~~~~~~~~~~~~~~~~~~~ +After completion of an automatic dump, or when a dump is aborted, the system +can either halt or reboot. In case of redundant servers, you may prefer halt +a buggy system, because another one ensures the service continues to work. +But in other cases, you may prefer rebooting to quickly restart services. +This is also configurable (read section 4). + + +4. How a crash can be prepared +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +4.1. Kernel options +~~~~~~~~~~~~~~~~~~~ + +First, choose the kernel compilation options which matches better your +situation. This may seem obvious, but you can reduce the risks of crash +by not enabling drivers designated for hardware you don't have. Specially +on servers, use only a reduced feature set, because you know exactly what +you need (eg: don't enable NTFS and QNX filesystems if you don't need them). + +Configure KMSGDUMP options to match your needs. Don't ask to auto-dump if +you don't have a floppy drive. In this case, you might prefer to enable +interactive mode to display messages on the screen and eventually print +them. + +When you use SCSI hard disks, you can sometimes reduce the reset time to +help the system recover faster. Eg: on my system, I have an AHA2940UW which +waits 15 seconds by default. All peripherals still work well with 1 second, +so 14 seconds are won. + +If you have changed your messages buffer size (which is 16 kB by default), +you should accord the size in "include/asm/kmsgdump.h", parameter LOG_BUG_LEN. +Some people required 32 kB. But you shouldn't exceed 60 kB since the dump is +done in real mode (16 bits). + +4.2. Configure KMSGDUMP +~~~~~~~~~~~~~~~~~~~~~~~ + +If your kernel supports SYSCTL, you can adjust KMSGDUMP parameters by +writing a string to /proc/sys/kernel/kmsgdump. This string consists in +a concatenation of flags. Most of them are only booleans. For each boolean, +a complementary flag exists to avoid any ambiguous interpretation. +For the moment, the flags are : + + Name Description Default Complement + F FAT mode Yes R + R Raw mode F + A Automatic mode Yes I + I Interactive mode A + B Boot after dump Yes H only used in automatic mode + H Halt after dump B only used in automatic mode + S Safe mode Yes O only used in automatic mode + O Overwrite disk S only used in automatic mode + E Enable dumping Yes D only used in automatic mode + D Disable dumping E only used in automatic mode + Txxx Track xxx 0 (N/A) first track is 0 per default + Uxxx Unit xxx 0 (N/A) bios drive is 0 (A:) per default + +Note: default means "default if none specified". + +Example: if you enter the following command, a kernel panic will generate + a dump in FAT mode after verifying that the disk has been prepared + for a dump, and then it will reboot : + + # echo "FABSE" > /proc/sys/kernel/kmsgdump + +This one will ask to dump raw messages at the end of the diskette in drive B +and halt : + + # echo "RABOET79U1" > /proc/sys/kernel/kmsgdump + +And this one will ask for a quick reboot : + + # echo "DB" > /proc/sys/kernel/kmsgdump + + +4.3. Prepare a disk for kmsgdump +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If safe mode is required, before an automatic dump, the system will read +the beginning of the floppy in the drive and will look for the word "KMSGDUMP" +at offset 3 of the first sector. This is the label of the diskette. The dump +will only be performed if this word is found as-is. So if you enable safe mode +don't forget to prepare your diskettes with the following command, provided +your diskette is in drive A : + + # echo "012KMSGDUMP" > /dev/fd0 + +Please note that when the dump is performed in FAT mode, this word is written +to the same place. This has two side effects : + - a diskette on which a dump has been done in FAT mode is re-usable without + intervention. + - you can prepare a diskette by entering kmsgdump (SysRQ+D) and doing a + FAT mode dump. + +On the other hand, when a RAW dump is done at the beginning of the disk, it +cannot be used again as a "safe kmsgdump disk". Moreover, letting it in the +drive when rebooting will cause the system to hang if the bios tries to boot +from the floppy first. + +4.4. Prepare the PC for a crash +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Because you'll have to leave a diskette in a drive, you may have to setup +your bios to boot from hard disk or anything but the floppy first because +the bios will find anything but a bootable system on this floppy. The problem +is with older systems on which the boot sequence cannot be changed. For this +reason, when a diskette is formated in FAT mode, a small code is inserted on +the boot sector which tries to redirect the boot to the first hard disk seen +by the bios. This is *generally* the bootable disk, but this may not be the +right on specific systems, so you may have to do some tests before considering +this option to be the right one for you. + +If your system is a server, you may reduce the time the bios tests the PC to +ensure quick reboot. On some systems, you can turn on the option "Quick +power-on self test", and disable testings of memory above 1MB. + + +5. Reading the messages back +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +5.1. FAT-formated disks +~~~~~~~~~~~~~~~~~~~~~~~ + +If the disk has been formated as FAT12, you'll find on it a file +named "MESSAGES.TXT" which contains all messages buffer. If the +buffer is not full, the end of the file is filled with zeroes, so +it's better to delete them using "tr" under linux. + + - under Linux, either mount the disk : + + # mount -rt msdos /dev/fd0 /mnt + # cat /mnt/messages.txt | tr -d '\000' + # umount /mnt + + or read it using mtools : + + # mtype a:messages.txt | tr -d '\000' + + - under DOS, you can simply run EDIT : + + C:\> edit a:messages.txt + + - under Windows, you can open the file with Wordpad. Avoid using + Notepad since it doesn't understand linefeeds only. + +5.2. RAW disks +~~~~~~~~~~~~~~ + +Raw disks will be readable under linux by using the utility DD. By +default, the dump will be performed from the first sector of the disk. +Example with 16 kB messages : + + # dd if=/dev/fd0 bs=512 count=32 | tr -d '\000' + +If you specified "T79" in the parameters to dump on track 79 of the disk, +you have to do some calculations : + +A 1.44 MB disk has 18 sectors/track, 2 heads and 512 bytes/sector so +18*2*512 equals 18432 bytes/track. You'll have to skip 18432 bytes for +each unwanted track. But you can also count only with kilobytes : if you +consider that a track is exactly 18 kilobytes, then skip the number of +tracks times 18 kilobytes : + + # expr 79 \* 18 + 1422 + # dd if=/dev/fd0 bs=1024 skip=1422 count=16 | tr -d '\000' + +The default dd utility reads all data from the start of the disk so this can +be quite long. There are other implementations on the net which do an "lseek" +before the first read. + + +6. Other speed improvements +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Here are some advices to make a system reboot faster. + +When a file server crashes, it may FSCK during a long time. There are good docs +about how to dramatically reduce FSCK time, but at least consider these +methods : + + - in /etc/fstab, set the sixth field (fs_passno) to 1 for the root fs, + and 2 for every other fs. FSCK will know it what it can parallelize + depending on hardware dependencies. In the better case, you can devide + the total time by the number of physical disks. + (man fstab and man fsck for more info). + + - when possible, mount filesystems read-only. On an anonymous FTP server, + for example, it's not always necessary to mount everything RW. So before + copying files onto an fs, remount it RW : + + # mount -wo remount /mount/point + + At the end, remount it RO : + + # mount -ro remount /mount/point + + - change the number of bytes by inode and the block size when formating + your FS. I personnaly use 16384 bytes/inode, a block size of 4096 bytes, + the sparse flag set (reduces the number of superblocks). This makes me + waste about 1% space, but total mount time is about 1 second for a total + of 8 FS's, 11 gigs on 5 separate disks and the total FSCK time after a + loosy power-off is less than 3 minutes. + +And of course, don't start services you don't need ! Sendmail itself can take +a long time if it cannot resolve the domain name. + + +7. For more information and/or suggestions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For more informations, you can email me at : + + willy@meta-x.org + +( be patient, I read my mail when I can, and can't always reply. I'm + used to "tail -1000 $MAIL|less" or "less +G $MAIL" ) + +For suggestions, you can either email them to me, or share them with +the Linux Kernel Mailing List : + + linux-kernel@vger.rutgers.edu + +Enjoy using it, + +Willy + diff -urN ikd-ref/Documentation/ktrace.txt ikd/Documentation/ktrace.txt --- ikd-ref/Documentation/ktrace.txt Thu Jan 1 01:00:00 1970 +++ ikd/Documentation/ktrace.txt Tue Oct 3 20:08:45 2000 @@ -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 ikd-ref/Documentation/sysrq.txt ikd/Documentation/sysrq.txt --- ikd-ref/Documentation/sysrq.txt Thu May 4 13:00:36 2000 +++ ikd/Documentation/sysrq.txt Tue Oct 3 20:08:45 2000 @@ -1,6 +1,7 @@ MAGIC SYSRQ KEY DOCUMENTATION v1.2 ------------------------------------ [Sat May 16 01:09:21 EDT 1998] + [Fri May 22 21:33:06 EST 1998 - add dumploGs, Oops] * What is the magic SysRQ key? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -35,7 +36,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. @@ -60,6 +61,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. + * Okay, so what can I use them for? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Well, un'R'aw is very handy when your X server or a svgalib program crashes. @@ -89,6 +94,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 ikd-ref/Makefile ikd/Makefile --- ikd-ref/Makefile Tue Oct 3 20:08:37 2000 +++ ikd/Makefile Tue Oct 3 20:08:45 2000 @@ -37,6 +37,7 @@ STRIP =$(CROSS_COMPILE)strip OBJCOPY =$(CROSS_COMPILE)objcopy OBJDUMP =$(CROSS_COMPILE)objdump +AWK =awk MAKE =make GENKSYMS=/sbin/genksyms @@ -95,7 +96,16 @@ # standard CFLAGS # -CFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +CFLAGS = -Wall -Wstrict-prototypes -O2 +ifeq ($(CONFIG_KERNEL_DEBUGGING),y) + CFLAGS += -fno-omit-frame-pointer + # 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 # use '-fno-strict-aliasing', but only if the compiler can take it CFLAGS += $(shell if $(CC) -fno-strict-aliasing -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-fno-strict-aliasing"; fi) @@ -116,6 +126,9 @@ # CORE_FILES =kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o +ifeq ($(CONFIG_KERNEL_DEBUGGING),y) + CORE_FILES +=kernel/debug/debug.o +endif FILESYSTEMS =fs/filesystems.a NETWORKS =net/network.a DRIVERS =drivers/block/block.a \ @@ -231,6 +244,38 @@ boot: vmlinux @$(MAKE) -C arch/$(ARCH)/boot +ifeq ($(CONFIG_KDB),y) +vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs + echo "c0000000 t firstaddr\n" > System.map + rm -f map map.out + $(AWK) -f scripts/genkdbsym.awk System.map > dummy_sym.c + $(CC) -c -o dummy_sym.o dummy_sym.c + $(LD) $(LINKFLAGS) $(HEAD) -Map map init/main.o init/version.o \ + --start-group \ + $(CORE_FILES) \ + $(FILESYSTEMS) \ + $(NETWORKS) \ + $(DRIVERS) \ + $(LIBS) \ + dummy_sym.o \ + --end-group \ + -o vmlinux + $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aU] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map + cp System.map System.map.sv + $(AWK) -f scripts/genkdbsym.awk System.map > ksym.c + $(CC) -c -o ksym.o ksym.c > ksym.o + $(LD) $(LINKFLAGS) $(HEAD) -Map map.out init/main.o init/version.o \ + --start-group \ + $(CORE_FILES) \ + $(FILESYSTEMS) \ + $(NETWORKS) \ + $(DRIVERS) \ + $(LIBS) \ + ksym.o \ + --end-group \ + -o vmlinux + $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aU] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map +else vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \ --start-group \ @@ -242,6 +287,7 @@ --end-group \ -o vmlinux $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aU] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map +endif symlinks: rm -f include/asm @@ -271,6 +317,10 @@ 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 + linuxsubdirs: $(patsubst %, _dir_%, $(SUBDIRS)) $(patsubst %, _dir_%, $(SUBDIRS)) : dummy include/config/MARKER @@ -394,6 +444,9 @@ rm -f core `find modules/ -type f -print`; \ fi rm -f submenu* + $(MAKE) -C scripts clean + $(MAKE) -C scripts/memleak clean + rm -f ksym.[ch] dummy_sym.c System.map.sv map map.out mrproper: clean archmrproper rm -f include/linux/autoconf.h include/linux/version.h @@ -438,7 +491,7 @@ scripts/mkdep `find $(FINDHPATH) -follow -name \*.h ! -name modversions.h -print` > .hdepend # set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i fastdep ;done # let this be made through the fastdep rule in Rules.make - $(MAKE) $(patsubst %,_sfdep_%,$(SUBDIRS)) _FASTDEP_ALL_SUB_DIRS="$(SUBDIRS)" + $(MAKE) $(patsubst %,_sfdep_%,$(SUBDIRS) scripts) _FASTDEP_ALL_SUB_DIRS="$(SUBDIRS) scripts" MODVERFILE := diff -urN ikd-ref/arch/alpha/config.in ikd/arch/alpha/config.in --- ikd-ref/arch/alpha/config.in Tue Oct 3 20:08:37 2000 +++ ikd/arch/alpha/config.in Tue Oct 3 20:08:45 2000 @@ -304,4 +304,7 @@ fi bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ + +source kernel/debug/Config.in + endmenu diff -urN ikd-ref/arch/alpha/kernel/entry.S ikd/arch/alpha/kernel/entry.S --- ikd-ref/arch/alpha/kernel/entry.S Thu May 4 13:00:36 2000 +++ ikd/arch/alpha/kernel/entry.S Tue Oct 3 20:08:45 2000 @@ -113,6 +113,17 @@ ldq $28,144($30); \ addq $30,184,$30 +/* + * Conditionally do profiling + */ +#ifdef CONFIG_TRACER +#define CALL_MCOUNT \ + lda $28,_mcount; \ + jsr $28,($28),_mcount +#else +#define CALL_MCOUNT +#endif + .text .set noat #if defined(__linux__) && !defined(__ELF__) @@ -135,6 +146,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) @@ -383,6 +396,11 @@ .ent entUna entUna: lda $30,-256($30) +#ifdef CONFIG_TRACER + 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) @@ -394,6 +412,10 @@ stq $5,40($30) stq $6,48($30) stq $7,56($30) +#ifndef CONFIG_TRACER + stq $8,64($30) + ldq $8,current_set +#endif stq $8,64($30) stq $9,72($30) stq $10,80($30) @@ -454,6 +476,9 @@ .ent entUnaUser entUnaUser: ldq $0,0($30) /* restore original $0 */ +#ifdef CONFIG_TRACER + ldq $8,64($30) +#endif lda $30,256($30) /* pop entUna's stack frame */ SAVE_ALL /* setup normal kernel stack */ lda $30,-56($30) @@ -586,6 +611,7 @@ beq $4,restore_all bne $5,signal_return restore_all: + CALL_MCOUNT RESTORE_ALL call_pal PAL_rti diff -urN ikd-ref/arch/alpha/lib/Makefile ikd/arch/alpha/lib/Makefile --- ikd-ref/arch/alpha/lib/Makefile Tue Jun 13 03:48:12 2000 +++ ikd/arch/alpha/lib/Makefile Tue Oct 3 20:08:45 2000 @@ -15,6 +15,10 @@ csum_ipv6_magic.o strcasecmp.o semaphore.o \ callback_srm.o callback_init.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 ikd-ref/arch/alpha/mm/fault.c ikd/arch/alpha/mm/fault.c --- ikd-ref/arch/alpha/mm/fault.c Tue Oct 3 20:08:37 2000 +++ ikd/arch/alpha/mm/fault.c Tue Oct 3 20:08:45 2000 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -92,6 +93,8 @@ return; } } + + prof_trap_entry(); down(&mm->mmap_sem); lock_kernel(); diff -urN ikd-ref/arch/i386/Makefile ikd/arch/i386/Makefile --- ikd-ref/arch/i386/Makefile Wed Aug 2 19:24:47 2000 +++ ikd/arch/i386/Makefile Tue Oct 3 20:08:45 2000 @@ -49,6 +49,12 @@ 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/kdb.a +CORE_FILES := $(CORE_FILES) arch/i386/kdb/kdb.o +SUBDIRS := $(SUBDIRS) arch/i386/kdb +endif + ifdef CONFIG_MATH_EMULATION SUBDIRS := $(SUBDIRS) arch/i386/math-emu DRIVERS := $(DRIVERS) arch/i386/math-emu/math.a @@ -59,6 +65,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 ikd-ref/arch/i386/boot/compressed/misc.c ikd/arch/i386/boot/compressed/misc.c --- ikd-ref/arch/i386/boot/compressed/misc.c Sun Apr 2 21:07:48 2000 +++ ikd/arch/i386/boot/compressed/misc.c Tue Oct 3 20:08:45 2000 @@ -104,7 +104,7 @@ #define LOW_BUFFER_START 0x2000 #define LOW_BUFFER_END 0x90000 #define LOW_BUFFER_SIZE ( LOW_BUFFER_END - LOW_BUFFER_START ) -#define HEAP_SIZE 0x2400 +#define HEAP_SIZE 0x3000 static int high_loaded =0; static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/; diff -urN ikd-ref/arch/i386/config.in ikd/arch/i386/config.in --- ikd-ref/arch/i386/config.in Tue Oct 3 20:08:37 2000 +++ ikd/arch/i386/config.in Tue Oct 3 20:08:45 2000 @@ -211,5 +211,29 @@ #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +bool 'Kernel messages dumper (EXPERIMENTAL)' CONFIG_KMSGDUMP +if [ "$CONFIG_KMSGDUMP" != "n" ]; then + bool ' Format floppies as FAT by default' CONFIG_KMSGDUMP_FAT + bool ' Automatically dump then reboot on kernel panic' CONFIG_KMSGDUMP_AUTO + bool ' Limit dump to pre-initialized floppies only' CONFIG_KMSGDUMP_SAFE +fi + +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 + bool ' Kernel memory leak detection support' CONFIG_MEMLEAK n + if [ "$CONFIG_SMP" = "y" ]; then + bool ' SMP-IOAPIC NMI Software Watchdog' CONFIG_NMI_WATCHDOG + if [ "$CONFIG_NMI_WATCHDOG" = "y" ]; then + int ' watchdog source IRQ' CONFIG_NMI_WATCHDOG_IRQ 0 + fi + fi + bool ' Built-in Kernel Debugger support' CONFIG_KDB n + if [ "$CONFIG_KDB" = "y" ]; then + define_bool CONFIG_KDB_FRAMEPTR y + fi +fi endmenu diff -urN ikd-ref/arch/i386/kdb/Makefile ikd/arch/i386/kdb/Makefile --- ikd-ref/arch/i386/kdb/Makefile Thu Jan 1 01:00:00 1970 +++ ikd/arch/i386/kdb/Makefile Tue Oct 3 20:08:45 2000 @@ -0,0 +1,23 @@ +# +# Makefile for i386-specific kdb files.. +# +# Copyright 1999, Silicon Graphics Inc. +# +# Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o + +L_TARGET = kdb.a +L_OBJS = kdb.o i386-dis.o kd_id.o kdbsupport.o kdb_io.o kdb_bp.o kdb_bt.o + +MOD_SUB_DIRS += modules + +override CFLAGS := $(CFLAGS:%-pg=%-g -c) + +include $(TOPDIR)/Rules.make diff -urN ikd-ref/arch/i386/kdb/dis-asm.h ikd/arch/i386/kdb/dis-asm.h --- ikd-ref/arch/i386/kdb/dis-asm.h Thu Jan 1 01:00:00 1970 +++ ikd/arch/i386/kdb/dis-asm.h Tue Oct 3 20:08:45 2000 @@ -0,0 +1,242 @@ +/* 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 */ + +#ifndef DIS_ASM_H +#define DIS_ASM_H + + /* + * Misc definitions + */ +#define ARGS(x) x +#define UNINITIALIZED(x) x +#define PTR void * +#define FILE int +#if !defined(NULL) +#define NULL 0 +#endif + +#include + +typedef int (*fprintf_ftype) PARAMS((FILE*, 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; + FILE *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; + /* The symbol at the start of the function being disassembled. This + is not set reliably, but if it is not NULL, it is correct. */ + asymbol *symbol; + + /* 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, 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; + 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; + + /* 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 */ + +} 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 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_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_m32r PARAMS ((bfd_vma, disassemble_info*)); +extern int print_insn_m88k 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 int print_insn_d10v 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*)); + +/* Fetch the disassembler for a given BFD, if that support is available. */ +extern disassembler_ftype disassembler PARAMS ((bfd *)); + + +/* 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 *, 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, \ + 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 seperatly. */ + +#define INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) \ + (INFO).fprintf_func = (FPRINTF_FUNC), \ + (INFO).stream = (STREAM), \ + (INFO).symbol = NULL, \ + (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 + +#endif /* ! defined (DIS_ASM_H) */ diff -urN ikd-ref/arch/i386/kdb/i386-dis.c ikd/arch/i386/kdb/i386-dis.c --- ikd-ref/arch/i386/kdb/i386-dis.c Thu Jan 1 01:00:00 1970 +++ ikd/arch/i386/kdb/i386-dis.c Tue Oct 3 20:08:45 2000 @@ -0,0 +1,2307 @@ +/* Print i386 instructions for GDB, the GNU debugger. + Copyright (C) 1988, 89, 91, 93, 94, 95, 96, 1997 + 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) + */ + +/* + * 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. + */ + +#include "dis-asm.h" +#if defined(__KERNEL__) +#include +#include +#endif + +#define MAXLEN 20 + +#if defined(STANDALONE) +#include +#endif /* STANDALONE */ + +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; +#if defined(STANDALONE) + jmp_buf bailout; +#endif /* STANDALONE */ +}; + +/* 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) + { + (*info->memory_error_func) (status, start, info); +#if defined(STANDALONE) + longjmp (priv->bailout, 1); +#else + /* XXX - what to do? */ + printk("Hmm. longjmp.\n"); +#endif + } + else + priv->max_fetched = addr; + return 1; +} + +#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 indirEv OP_indirE, v_mode +#define Ew OP_E, w_mode +#define Ma OP_E, v_mode +#define M OP_E, 0 +#define Mp OP_E, 0 /* ? */ +#define Gv OP_G, v_mode +#define Gw OP_G, w_mode +#define Rw OP_rm, w_mode +#define Rd OP_rm, 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 +#if 0 +#define ONE OP_ONE, 0 +#endif +#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, lptr +#define Av OP_DIR, v_mode +#define Ob OP_OFF, b_mode +#define Ov OP_OFF, v_mode +#define Xb OP_DSSI, b_mode +#define Xv OP_DSSI, v_mode +#define Yb OP_ESDI, b_mode +#define Yv OP_ESDI, v_mode + +#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 EM OP_EM, v_mode +#define MS OP_MS, b_mode + +typedef int (*op_rtn) PARAMS ((int bytemode, int aflag, int dflag)); + +static int OP_E PARAMS ((int, int, int)); +static int OP_G PARAMS ((int, int, int)); +static int OP_I PARAMS ((int, int, int)); +static int OP_indirE PARAMS ((int, int, int)); +static int OP_sI PARAMS ((int, int, int)); +static int OP_REG PARAMS ((int, int, int)); +static int OP_J PARAMS ((int, int, int)); +static int OP_DIR PARAMS ((int, int, int)); +static int OP_OFF PARAMS ((int, int, int)); +static int OP_ESDI PARAMS ((int, int, int)); +static int OP_DSSI PARAMS ((int, int, int)); +static int OP_SEG PARAMS ((int, int, int)); +static int OP_C PARAMS ((int, int, int)); +static int OP_D PARAMS ((int, int, int)); +static int OP_T PARAMS ((int, int, int)); +static int OP_rm PARAMS ((int, int, int)); +static int OP_ST PARAMS ((int, int, int)); +static int OP_STi PARAMS ((int, int, int)); +#if 0 +static int OP_ONE PARAMS ((int, int, int)); +#endif +static int OP_MMX PARAMS ((int, int, int)); +static int OP_EM PARAMS ((int, int, int)); +static int OP_MS PARAMS ((int, int, int)); + +static void append_prefix PARAMS ((void)); +static void set_op PARAMS ((int op)); +static void putop PARAMS ((char *template, int aflag, int dflag)); +static void dofloat PARAMS ((int aflag, int dflag)); +static int get16 PARAMS ((void)); +static int get32 PARAMS ((void)); +static void ckprefix PARAMS ((void)); + +#define b_mode 1 +#define v_mode 2 +#define w_mode 3 +#define d_mode 4 + +#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 107 +#define eCX_reg 108 +#define eDX_reg 109 +#define eBX_reg 110 +#define eSP_reg 111 +#define eBP_reg 112 +#define eSI_reg 113 +#define eDI_reg 114 + +#define lptr 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 GRP1b NULL, NULL, 0 +#define GRP1S NULL, NULL, 1 +#define GRP1Ss NULL, NULL, 2 +#define GRP2b NULL, NULL, 3 +#define GRP2S NULL, NULL, 4 +#define GRP2b_one NULL, NULL, 5 +#define GRP2S_one NULL, NULL, 6 +#define GRP2b_cl NULL, NULL, 7 +#define GRP2S_cl NULL, NULL, 8 +#define GRP3b NULL, NULL, 9 +#define GRP3S NULL, NULL, 10 +#define GRP4 NULL, NULL, 11 +#define GRP5 NULL, NULL, 12 +#define GRP6 NULL, NULL, 13 +#define GRP7 NULL, NULL, 14 +#define GRP8 NULL, NULL, 15 +#define GRP9 NULL, NULL, 16 +#define GRP10 NULL, NULL, 17 +#define GRP11 NULL, NULL, 18 +#define GRP12 NULL, NULL, 19 + +#define FLOATCODE 50 +#define FLOAT NULL, NULL, FLOATCODE + +struct dis386 { + char *name; + op_rtn op1; + int bytemode1; + op_rtn op2; + int bytemode2; + op_rtn op3; + int bytemode3; +}; + +static struct dis386 dis386[] = { + /* 00 */ + { "addb", Eb, Gb }, + { "addS", Ev, Gv }, + { "addb", Gb, Eb }, + { "addS", Gv, Ev }, + { "addb", AL, Ib }, + { "addS", eAX, Iv }, + { "pushS", es }, + { "popS", es }, + /* 08 */ + { "orb", Eb, Gb }, + { "orS", Ev, Gv }, + { "orb", Gb, Eb }, + { "orS", Gv, Ev }, + { "orb", AL, Ib }, + { "orS", eAX, Iv }, + { "pushS", cs }, + { "(bad)" }, /* 0x0f extended opcode escape */ + /* 10 */ + { "adcb", Eb, Gb }, + { "adcS", Ev, Gv }, + { "adcb", Gb, Eb }, + { "adcS", Gv, Ev }, + { "adcb", AL, Ib }, + { "adcS", eAX, Iv }, + { "pushS", ss }, + { "popS", ss }, + /* 18 */ + { "sbbb", Eb, Gb }, + { "sbbS", Ev, Gv }, + { "sbbb", Gb, Eb }, + { "sbbS", Gv, Ev }, + { "sbbb", AL, Ib }, + { "sbbS", eAX, Iv }, + { "pushS", ds }, + { "popS", ds }, + /* 20 */ + { "andb", Eb, Gb }, + { "andS", Ev, Gv }, + { "andb", Gb, Eb }, + { "andS", Gv, Ev }, + { "andb", AL, Ib }, + { "andS", eAX, Iv }, + { "(bad)" }, /* SEG ES prefix */ + { "daa" }, + /* 28 */ + { "subb", Eb, Gb }, + { "subS", Ev, Gv }, + { "subb", Gb, Eb }, + { "subS", Gv, Ev }, + { "subb", AL, Ib }, + { "subS", eAX, Iv }, + { "(bad)" }, /* SEG CS prefix */ + { "das" }, + /* 30 */ + { "xorb", Eb, Gb }, + { "xorS", Ev, Gv }, + { "xorb", Gb, Eb }, + { "xorS", Gv, Ev }, + { "xorb", AL, Ib }, + { "xorS", eAX, Iv }, + { "(bad)" }, /* SEG SS prefix */ + { "aaa" }, + /* 38 */ + { "cmpb", Eb, Gb }, + { "cmpS", Ev, Gv }, + { "cmpb", Gb, Eb }, + { "cmpS", Gv, Ev }, + { "cmpb", AL, Ib }, + { "cmpS", eAX, Iv }, + { "(bad)" }, /* SEG DS prefix */ + { "aas" }, + /* 40 */ + { "incS", eAX }, + { "incS", eCX }, + { "incS", eDX }, + { "incS", eBX }, + { "incS", eSP }, + { "incS", eBP }, + { "incS", eSI }, + { "incS", eDI }, + /* 48 */ + { "decS", eAX }, + { "decS", eCX }, + { "decS", eDX }, + { "decS", eBX }, + { "decS", eSP }, + { "decS", eBP }, + { "decS", eSI }, + { "decS", eDI }, + /* 50 */ + { "pushS", eAX }, + { "pushS", eCX }, + { "pushS", eDX }, + { "pushS", eBX }, + { "pushS", eSP }, + { "pushS", eBP }, + { "pushS", eSI }, + { "pushS", eDI }, + /* 58 */ + { "popS", eAX }, + { "popS", eCX }, + { "popS", eDX }, + { "popS", eBX }, + { "popS", eSP }, + { "popS", eBP }, + { "popS", eSI }, + { "popS", eDI }, + /* 60 */ + { "pusha" }, + { "popa" }, + { "boundS", Gv, Ma }, + { "arpl", Ew, Gw }, + { "(bad)" }, /* seg fs */ + { "(bad)" }, /* seg gs */ + { "(bad)" }, /* op size prefix */ + { "(bad)" }, /* adr size prefix */ + /* 68 */ + { "pushS", Iv }, /* 386 book wrong */ + { "imulS", Gv, Ev, Iv }, + { "pushS", sIb }, /* push of byte really pushes 2 or 4 bytes */ + { "imulS", Gv, Ev, Ib }, + { "insb", Yb, indirDX }, + { "insS", Yv, indirDX }, + { "outsb", indirDX, Xb }, + { "outsS", indirDX, Xv }, + /* 70 */ + { "jo", Jb }, + { "jno", Jb }, + { "jb", Jb }, + { "jae", Jb }, + { "je", Jb }, + { "jne", Jb }, + { "jbe", Jb }, + { "ja", Jb }, + /* 78 */ + { "js", Jb }, + { "jns", Jb }, + { "jp", Jb }, + { "jnp", Jb }, + { "jl", Jb }, + { "jnl", Jb }, + { "jle", Jb }, + { "jg", Jb }, + /* 80 */ + { GRP1b }, + { GRP1S }, + { "(bad)" }, + { GRP1Ss }, + { "testb", Eb, Gb }, + { "testS", Ev, Gv }, + { "xchgb", Eb, Gb }, + { "xchgS", Ev, Gv }, + /* 88 */ + { "movb", Eb, Gb }, + { "movS", Ev, Gv }, + { "movb", Gb, Eb }, + { "movS", Gv, Ev }, + { "movw", Ew, Sw }, + { "leaS", Gv, M }, + { "movw", Sw, Ew }, + { "popS", Ev }, + /* 90 */ + { "nop" }, + { "xchgS", eCX, eAX }, + { "xchgS", eDX, eAX }, + { "xchgS", eBX, eAX }, + { "xchgS", eSP, eAX }, + { "xchgS", eBP, eAX }, + { "xchgS", eSI, eAX }, + { "xchgS", eDI, eAX }, + /* 98 */ + { "cWtS" }, + { "cStd" }, + { "lcall", Ap }, + { "(bad)" }, /* fwait */ + { "pushf" }, + { "popf" }, + { "sahf" }, + { "lahf" }, + /* a0 */ + { "movb", AL, Ob }, + { "movS", eAX, Ov }, + { "movb", Ob, AL }, + { "movS", Ov, eAX }, + { "movsb", Yb, Xb }, + { "movsS", Yv, Xv }, + { "cmpsb", Yb, Xb }, + { "cmpsS", Yv, Xv }, + /* a8 */ + { "testb", AL, Ib }, + { "testS", eAX, Iv }, + { "stosb", Yb, AL }, + { "stosS", Yv, eAX }, + { "lodsb", AL, Xb }, + { "lodsS", eAX, Xv }, + { "scasb", AL, Yb }, + { "scasS", eAX, Yv }, + /* b0 */ + { "movb", AL, Ib }, + { "movb", CL, Ib }, + { "movb", DL, Ib }, + { "movb", BL, Ib }, + { "movb", AH, Ib }, + { "movb", CH, Ib }, + { "movb", DH, Ib }, + { "movb", BH, Ib }, + /* b8 */ + { "movS", eAX, Iv }, + { "movS", eCX, Iv }, + { "movS", eDX, Iv }, + { "movS", eBX, Iv }, + { "movS", eSP, Iv }, + { "movS", eBP, Iv }, + { "movS", eSI, Iv }, + { "movS", eDI, Iv }, + /* c0 */ + { GRP2b }, + { GRP2S }, + { "ret", Iw }, + { "ret" }, + { "lesS", Gv, Mp }, + { "ldsS", Gv, Mp }, + { "movb", Eb, Ib }, + { "movS", Ev, Iv }, + /* c8 */ + { "enter", Iw, Ib }, + { "leave" }, + { "lret", Iw }, + { "lret" }, + { "int3" }, + { "int", Ib }, + { "into" }, + { "iret" }, + /* d0 */ + { GRP2b_one }, + { GRP2S_one }, + { GRP2b_cl }, + { GRP2S_cl }, + { "aam", Ib }, + { "aad", Ib }, + { "(bad)" }, + { "xlat" }, + /* d8 */ + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + /* e0 */ + { "loopne", Jb }, + { "loope", Jb }, + { "loop", Jb }, + { "jCcxz", Jb }, + { "inb", AL, Ib }, + { "inS", eAX, Ib }, + { "outb", Ib, AL }, + { "outS", Ib, eAX }, + /* e8 */ + { "call", Av }, + { "jmp", Jv }, + { "ljmp", Ap }, + { "jmp", Jb }, + { "inb", AL, indirDX }, + { "inS", eAX, indirDX }, + { "outb", indirDX, AL }, + { "outS", indirDX, eAX }, + /* f0 */ + { "(bad)" }, /* lock prefix */ + { "(bad)" }, + { "(bad)" }, /* repne */ + { "(bad)" }, /* repz */ + { "hlt" }, + { "cmc" }, + { GRP3b }, + { GRP3S }, + /* f8 */ + { "clc" }, + { "stc" }, + { "cli" }, + { "sti" }, + { "cld" }, + { "std" }, + { GRP4 }, + { GRP5 }, +}; + +static struct dis386 dis386_twobyte[] = { + /* 00 */ + { GRP6 }, + { GRP7 }, + { "larS", Gv, Ew }, + { "lslS", Gv, Ew }, + { "(bad)" }, + { "(bad)" }, + { "clts" }, + { "(bad)" }, + /* 08 */ + { "invd" }, + { "wbinvd" }, + { "(bad)" }, { "ud2a" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 10 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 18 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 20 */ + /* these are all backward in appendix A of the intel book */ + { "movl", Rd, Cd }, + { "movl", Rd, Dd }, + { "movl", Cd, Rd }, + { "movl", Dd, Rd }, + { "movl", Rd, Td }, + { "(bad)" }, + { "movl", Td, Rd }, + { "(bad)" }, + /* 28 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 30 */ + { "wrmsr" }, { "rdtsc" }, { "rdmsr" }, { "rdpmc" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 38 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 40 */ + { "cmovo", Gv,Ev }, { "cmovno", Gv,Ev }, { "cmovb", Gv,Ev }, { "cmovae", Gv,Ev }, + { "cmove", Gv,Ev }, { "cmovne", Gv,Ev }, { "cmovbe", Gv,Ev }, { "cmova", Gv,Ev }, + /* 48 */ + { "cmovs", Gv,Ev }, { "cmovns", Gv,Ev }, { "cmovp", Gv,Ev }, { "cmovnp", Gv,Ev }, + { "cmovl", Gv,Ev }, { "cmovge", Gv,Ev }, { "cmovle", Gv,Ev }, { "cmovg", Gv,Ev }, + /* 50 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 58 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 60 */ + { "punpcklbw", MX, EM }, + { "punpcklwd", MX, EM }, + { "punpckldq", MX, EM }, + { "packsswb", MX, EM }, + { "pcmpgtb", MX, EM }, + { "pcmpgtw", MX, EM }, + { "pcmpgtd", MX, EM }, + { "packuswb", MX, EM }, + /* 68 */ + { "punpckhbw", MX, EM }, + { "punpckhwd", MX, EM }, + { "punpckhdq", MX, EM }, + { "packssdw", MX, EM }, + { "(bad)" }, { "(bad)" }, + { "movd", MX, Ev }, + { "movq", MX, EM }, + /* 70 */ + { "(bad)" }, + { GRP10 }, + { GRP11 }, + { GRP12 }, + { "pcmpeqb", MX, EM }, + { "pcmpeqw", MX, EM }, + { "pcmpeqd", MX, EM }, + { "emms" }, + /* 78 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, + { "movd", Ev, MX }, + { "movq", EM, MX }, + /* 80 */ + { "jo", Jv }, + { "jno", Jv }, + { "jb", Jv }, + { "jae", Jv }, + { "je", Jv }, + { "jne", Jv }, + { "jbe", Jv }, + { "ja", Jv }, + /* 88 */ + { "js", Jv }, + { "jns", Jv }, + { "jp", Jv }, + { "jnp", Jv }, + { "jl", Jv }, + { "jge", Jv }, + { "jle", Jv }, + { "jg", Jv }, + /* 90 */ + { "seto", Eb }, + { "setno", Eb }, + { "setb", Eb }, + { "setae", Eb }, + { "sete", Eb }, + { "setne", Eb }, + { "setbe", Eb }, + { "seta", Eb }, + /* 98 */ + { "sets", Eb }, + { "setns", Eb }, + { "setp", Eb }, + { "setnp", Eb }, + { "setl", Eb }, + { "setge", Eb }, + { "setle", Eb }, + { "setg", Eb }, + /* a0 */ + { "pushS", fs }, + { "popS", fs }, + { "cpuid" }, + { "btS", Ev, Gv }, + { "shldS", Ev, Gv, Ib }, + { "shldS", Ev, Gv, CL }, + { "(bad)" }, + { "(bad)" }, + /* a8 */ + { "pushS", gs }, + { "popS", gs }, + { "rsm" }, + { "btsS", Ev, Gv }, + { "shrdS", Ev, Gv, Ib }, + { "shrdS", Ev, Gv, CL }, + { "(bad)" }, + { "imulS", Gv, Ev }, + /* b0 */ + { "cmpxchgb", Eb, Gb }, + { "cmpxchgS", Ev, Gv }, + { "lssS", Gv, Mp }, /* 386 lists only Mp */ + { "btrS", Ev, Gv }, + { "lfsS", Gv, Mp }, /* 386 lists only Mp */ + { "lgsS", Gv, Mp }, /* 386 lists only Mp */ + { "movzbS", Gv, Eb }, + { "movzwS", Gv, Ew }, + /* b8 */ + { "ud2b" }, + { "(bad)" }, + { GRP8 }, + { "btcS", Ev, Gv }, + { "bsfS", Gv, Ev }, + { "bsrS", Gv, Ev }, + { "movsbS", Gv, Eb }, + { "movswS", Gv, Ew }, + /* c0 */ + { "xaddb", Eb, Gb }, + { "xaddS", Ev, Gv }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { GRP9 }, + /* c8 */ + { "bswap", eAX }, + { "bswap", eCX }, + { "bswap", eDX }, + { "bswap", eBX }, + { "bswap", eSP }, + { "bswap", eBP }, + { "bswap", eSI }, + { "bswap", eDI }, + /* d0 */ + { "(bad)" }, + { "psrlw", MX, EM }, + { "psrld", MX, EM }, + { "psrlq", MX, EM }, + { "(bad)" }, + { "pmullw", MX, EM }, + { "(bad)" }, { "(bad)" }, + /* d8 */ + { "psubusb", MX, EM }, + { "psubusw", MX, EM }, + { "(bad)" }, + { "pand", MX, EM }, + { "paddusb", MX, EM }, + { "paddusw", MX, EM }, + { "(bad)" }, + { "pandn", MX, EM }, + /* e0 */ + { "(bad)" }, + { "psraw", MX, EM }, + { "psrad", MX, EM }, + { "(bad)" }, + { "(bad)" }, + { "pmulhw", MX, EM }, + { "(bad)" }, { "(bad)" }, + /* e8 */ + { "psubsb", MX, EM }, + { "psubsw", MX, EM }, + { "(bad)" }, + { "por", MX, EM }, + { "paddsb", MX, EM }, + { "paddsw", MX, EM }, + { "(bad)" }, + { "pxor", MX, EM }, + /* f0 */ + { "(bad)" }, + { "psllw", MX, EM }, + { "pslld", MX, EM }, + { "psllq", MX, EM }, + { "(bad)" }, + { "pmaddwd", MX, EM }, + { "(bad)" }, { "(bad)" }, + /* f8 */ + { "psubb", MX, EM }, + { "psubw", MX, EM }, + { "psubd", MX, EM }, + { "(bad)" }, + { "paddb", MX, EM }, + { "paddw", MX, EM }, + { "paddd", MX, EM }, + { "(bad)" } +}; + +static const unsigned char onebyte_has_modrm[256] = { + 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, + 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, + 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, + 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, + 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1 +}; + +static const unsigned char twobyte_has_modrm[256] = { + /* 00 */ 1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */ + /* 10 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */ + /* 20 */ 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, /* 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 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */ + /* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1, /* 6f */ + /* 70 */ 0,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,0,1,1,0,1,1,1,0,1, /* df */ + /* e0 */ 0,1,1,0,0,1,0,0,1,1,0,1,1,1,0,1, /* ef */ + /* f0 */ 0,1,1,1,0,1,0,0,1,1,1,0,1,1,1,0 /* ff */ +}; + +static char obuf[100]; +static char *obufp; +static char scratchbuf[100]; +static unsigned char *start_codep; +static unsigned char *codep; +static disassemble_info *the_info; +static int mod; +static int rm; +static int reg; +static void oappend PARAMS ((char *s)); + +static char *names32[]={ + "%eax","%ecx","%edx","%ebx", "%esp","%ebp","%esi","%edi", +}; +static char *names16[] = { + "%ax","%cx","%dx","%bx","%sp","%bp","%si","%di", +}; +static char *names8[] = { + "%al","%cl","%dl","%bl","%ah","%ch","%dh","%bh", +}; +static char *names_seg[] = { + "%es","%cs","%ss","%ds","%fs","%gs","%?","%?", +}; +static char *index16[] = { + "bx+si","bx+di","bp+si","bp+di","si","di","bp","bx" +}; + +static struct dis386 grps[][8] = { + /* GRP1b */ + { + { "addb", Eb, Ib }, + { "orb", Eb, Ib }, + { "adcb", Eb, Ib }, + { "sbbb", Eb, Ib }, + { "andb", Eb, Ib }, + { "subb", Eb, Ib }, + { "xorb", Eb, Ib }, + { "cmpb", Eb, Ib } + }, + /* GRP1S */ + { + { "addS", Ev, Iv }, + { "orS", Ev, Iv }, + { "adcS", Ev, Iv }, + { "sbbS", Ev, Iv }, + { "andS", Ev, Iv }, + { "subS", Ev, Iv }, + { "xorS", Ev, Iv }, + { "cmpS", Ev, Iv } + }, + /* GRP1Ss */ + { + { "addS", Ev, sIb }, + { "orS", Ev, sIb }, + { "adcS", Ev, sIb }, + { "sbbS", Ev, sIb }, + { "andS", Ev, sIb }, + { "subS", Ev, sIb }, + { "xorS", Ev, sIb }, + { "cmpS", Ev, sIb } + }, + /* GRP2b */ + { + { "rolb", Eb, Ib }, + { "rorb", Eb, Ib }, + { "rclb", Eb, Ib }, + { "rcrb", Eb, Ib }, + { "shlb", Eb, Ib }, + { "shrb", Eb, Ib }, + { "(bad)" }, + { "sarb", Eb, Ib }, + }, + /* GRP2S */ + { + { "rolS", Ev, Ib }, + { "rorS", Ev, Ib }, + { "rclS", Ev, Ib }, + { "rcrS", Ev, Ib }, + { "shlS", Ev, Ib }, + { "shrS", Ev, Ib }, + { "(bad)" }, + { "sarS", Ev, Ib }, + }, + /* GRP2b_one */ + { + { "rolb", Eb }, + { "rorb", Eb }, + { "rclb", Eb }, + { "rcrb", Eb }, + { "shlb", Eb }, + { "shrb", Eb }, + { "(bad)" }, + { "sarb", Eb }, + }, + /* GRP2S_one */ + { + { "rolS", Ev }, + { "rorS", Ev }, + { "rclS", Ev }, + { "rcrS", Ev }, + { "shlS", Ev }, + { "shrS", Ev }, + { "(bad)" }, + { "sarS", Ev }, + }, + /* GRP2b_cl */ + { + { "rolb", Eb, CL }, + { "rorb", Eb, CL }, + { "rclb", Eb, CL }, + { "rcrb", Eb, CL }, + { "shlb", Eb, CL }, + { "shrb", Eb, CL }, + { "(bad)" }, + { "sarb", Eb, CL }, + }, + /* GRP2S_cl */ + { + { "rolS", Ev, CL }, + { "rorS", Ev, CL }, + { "rclS", Ev, CL }, + { "rcrS", Ev, CL }, + { "shlS", Ev, CL }, + { "shrS", Ev, CL }, + { "(bad)" }, + { "sarS", Ev, CL } + }, + /* GRP3b */ + { + { "testb", Eb, Ib }, + { "(bad)", Eb }, + { "notb", Eb }, + { "negb", Eb }, + { "mulb", AL, Eb }, + { "imulb", AL, Eb }, + { "divb", AL, Eb }, + { "idivb", AL, Eb } + }, + /* GRP3S */ + { + { "testS", Ev, Iv }, + { "(bad)" }, + { "notS", Ev }, + { "negS", Ev }, + { "mulS", eAX, Ev }, + { "imulS", eAX, Ev }, + { "divS", eAX, Ev }, + { "idivS", eAX, Ev }, + }, + /* GRP4 */ + { + { "incb", Eb }, + { "decb", Eb }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + }, + /* GRP5 */ + { + { "incS", Ev }, + { "decS", Ev }, + { "call", indirEv }, + { "lcall", indirEv }, + { "jmp", indirEv }, + { "ljmp", indirEv }, + { "pushS", Ev }, + { "(bad)" }, + }, + /* GRP6 */ + { + { "sldt", Ew }, + { "str", Ew }, + { "lldt", Ew }, + { "ltr", Ew }, + { "verr", Ew }, + { "verw", Ew }, + { "(bad)" }, + { "(bad)" } + }, + /* GRP7 */ + { + { "sgdt", Ew }, + { "sidt", Ew }, + { "lgdt", Ew }, + { "lidt", Ew }, + { "smsw", Ew }, + { "(bad)" }, + { "lmsw", Ew }, + { "invlpg", Ew }, + }, + /* GRP8 */ + { + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "btS", Ev, Ib }, + { "btsS", Ev, Ib }, + { "btrS", Ev, Ib }, + { "btcS", Ev, Ib }, + }, + /* GRP9 */ + { + { "(bad)" }, + { "cmpxchg8b", Ev }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + }, + /* GRP10 */ + { + { "(bad)" }, + { "(bad)" }, + { "psrlw", MS, Ib }, + { "(bad)" }, + { "psraw", MS, Ib }, + { "(bad)" }, + { "psllw", MS, Ib }, + { "(bad)" }, + }, + /* GRP11 */ + { + { "(bad)" }, + { "(bad)" }, + { "psrld", MS, Ib }, + { "(bad)" }, + { "psrad", MS, Ib }, + { "(bad)" }, + { "pslld", MS, Ib }, + { "(bad)" }, + }, + /* GRP12 */ + { + { "(bad)" }, + { "(bad)" }, + { "psrlq", MS, Ib }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "psllq", MS, Ib }, + { "(bad)" }, + } +}; + +#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_ADR 0x400 +#define PREFIX_FWAIT 0x800 + +static int prefixes; + +static void +ckprefix () +{ + 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_ADR; + break; + case 0x9b: + prefixes |= PREFIX_FWAIT; + break; + default: + return; + } + codep++; + } +} + +static char op1out[100], op2out[100], op3out[100]; +static int op_address[3], op_ad, op_index[3]; +static 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. + */ + +int print_insn_x86 PARAMS ((bfd_vma pc, disassemble_info *info, int aflag, + int dflag)); +int +print_insn_i386 (pc, info) + bfd_vma pc; + disassemble_info *info; +{ + if (info->mach == bfd_mach_i386_i386) + return print_insn_x86 (pc, info, 1, 1); + else if (info->mach == bfd_mach_i386_i8086) + return print_insn_x86 (pc, info, 0, 0); + else +#if defined(__KERNEL__) + printk("Bad machine type\n"); +#else + abort (); +#endif +} + +int +print_insn_x86 (pc, info, aflag, dflag) + bfd_vma pc; + disassemble_info *info; +{ + struct dis386 *dp; + int i; + int enter_instruction; + char *first, *second, *third; + int needcomma; + unsigned char need_modrm; + + struct dis_private priv; + bfd_byte *inbuf = priv.the_buffer; + + /* The output looks better if we put 5 bytes on a line, since that + puts long word instructions on a single line. */ + info->bytes_per_line = 5; + + info->private_data = (PTR) &priv; + priv.max_fetched = priv.the_buffer; + priv.insn_start = pc; +#if defined(STANDALONE) + if (setjmp (priv.bailout) != 0) + /* Error return. */ + return -1; +#endif + + 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; + + ckprefix (); + + FETCH_DATA (info, codep + 1); + if (*codep == 0xc8) + enter_instruction = 1; + else + enter_instruction = 0; + + obufp = obuf; + + if (prefixes & PREFIX_REPZ) + oappend ("repz "); + if (prefixes & PREFIX_REPNZ) + oappend ("repnz "); + if (prefixes & PREFIX_LOCK) + oappend ("lock "); + + if ((prefixes & PREFIX_FWAIT) + && ((*codep < 0xd8) || (*codep > 0xdf))) + { + /* fwait not followed by floating point instruction */ + (*info->fprintf_func) (info->stream, "fwait"); + return (1); + } + + if (prefixes & PREFIX_DATA) + dflag ^= 1; + + if (prefixes & PREFIX_ADR) + { + aflag ^= 1; + if (aflag) + oappend ("addr32 "); + else + oappend ("addr16 "); + } + + if (*codep == 0x0f) + { + FETCH_DATA (info, codep + 2); + dp = &dis386_twobyte[*++codep]; + need_modrm = twobyte_has_modrm[*codep]; + } + else + { + dp = &dis386[*codep]; + need_modrm = onebyte_has_modrm[*codep]; + } + codep++; + + 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 (aflag, dflag); + } + else + { + if (dp->name == NULL) + dp = &grps[dp->bytemode1][reg]; + + putop (dp->name, aflag, dflag); + + obufp = op1out; + op_ad = 2; + if (dp->op1) + (*dp->op1)(dp->bytemode1, aflag, dflag); + + obufp = op2out; + op_ad = 1; + if (dp->op2) + (*dp->op2)(dp->bytemode2, aflag, dflag); + + obufp = op3out; + op_ad = 0; + if (dp->op3) + (*dp->op3)(dp->bytemode3, aflag, dflag); + } + + obufp = obuf + strlen (obuf); + for (i = strlen (obuf); i < 6; i++) + oappend (" "); + oappend (" "); + (*info->fprintf_func) (info->stream, "%s", obuf); + + /* enter instruction is printed with operands in the + * same order as the intel book; everything else + * is printed in reverse order + */ + if (enter_instruction) + { + 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) (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) (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) (op_address[op_index[2]], info); + else + (*info->fprintf_func) (info->stream, "%s", third); + } + return (codep - inbuf); +} + +static char *float_mem[] = { + /* 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", +}; + +#define ST OP_ST, 0 +#define STi OP_STi, 0 + +#define FGRPd9_2 NULL, NULL, 0 +#define FGRPd9_4 NULL, NULL, 1 +#define FGRPd9_5 NULL, NULL, 2 +#define FGRPd9_6 NULL, NULL, 3 +#define FGRPd9_7 NULL, NULL, 4 +#define FGRPda_5 NULL, NULL, 5 +#define FGRPdb_4 NULL, NULL, 6 +#define FGRPde_3 NULL, NULL, 7 +#define FGRPdf_4 NULL, NULL, 8 + +static struct dis386 float_reg[][8] = { + /* d8 */ + { + { "fadd", ST, STi }, + { "fmul", ST, STi }, + { "fcom", STi }, + { "fcomp", STi }, + { "fsub", ST, STi }, + { "fsubr", ST, STi }, + { "fdiv", ST, STi }, + { "fdivr", ST, STi }, + }, + /* d9 */ + { + { "fld", STi }, + { "fxch", STi }, + { FGRPd9_2 }, + { "(bad)" }, + { FGRPd9_4 }, + { FGRPd9_5 }, + { FGRPd9_6 }, + { FGRPd9_7 }, + }, + /* da */ + { + { "fcmovb", ST, STi }, + { "fcmove", ST, STi }, + { "fcmovbe",ST, STi }, + { "fcmovu", ST, STi }, + { "(bad)" }, + { FGRPda_5 }, + { "(bad)" }, + { "(bad)" }, + }, + /* db */ + { + { "fcmovnb",ST, STi }, + { "fcmovne",ST, STi }, + { "fcmovnbe",ST, STi }, + { "fcmovnu",ST, STi }, + { FGRPdb_4 }, + { "fucomi", ST, STi }, + { "fcomi", ST, STi }, + { "(bad)" }, + }, + /* dc */ + { + { "fadd", STi, ST }, + { "fmul", STi, ST }, + { "(bad)" }, + { "(bad)" }, + { "fsub", STi, ST }, + { "fsubr", STi, ST }, + { "fdiv", STi, ST }, + { "fdivr", STi, ST }, + }, + /* dd */ + { + { "ffree", STi }, + { "(bad)" }, + { "fst", STi }, + { "fstp", STi }, + { "fucom", STi }, + { "fucomp", STi }, + { "(bad)" }, + { "(bad)" }, + }, + /* de */ + { + { "faddp", STi, ST }, + { "fmulp", STi, ST }, + { "(bad)" }, + { FGRPde_3 }, + { "fsubp", STi, ST }, + { "fsubrp", STi, ST }, + { "fdivp", STi, ST }, + { "fdivrp", STi, ST }, + }, + /* df */ + { + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { FGRPdf_4 }, + { "fucomip",ST, STi }, + { "fcomip", ST, STi }, + { "(bad)" }, + }, +}; + + +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 (aflag, dflag) + int aflag; + int dflag; +{ + struct dis386 *dp; + unsigned char floatop; + + floatop = codep[-1]; + + if (mod != 3) + { + putop (float_mem[(floatop - 0xd8) * 8 + reg], aflag, dflag); + obufp = op1out; + OP_E (v_mode, aflag, dflag); + return; + } + codep++; + + dp = &float_reg[floatop - 0xd8][reg]; + if (dp->name == NULL) + { + putop (fgrps[dp->bytemode1][rm], aflag, dflag); + /* instruction fnstsw is only one with strange arg */ + if (floatop == 0xdf + && FETCH_DATA (the_info, codep + 1) + && *codep == 0xe0) + strcpy (op1out, "%eax"); + } + else + { + putop (dp->name, aflag, dflag); + obufp = op1out; + if (dp->op1) + (*dp->op1)(dp->bytemode1, aflag, dflag); + obufp = op2out; + if (dp->op2) + (*dp->op2)(dp->bytemode2, aflag, dflag); + } +} + +/* ARGSUSED */ +static int +OP_ST (ignore, aflag, dflag) + int ignore; + int aflag; + int dflag; +{ + oappend ("%st"); + return (0); +} + +/* ARGSUSED */ +static int +OP_STi (ignore, aflag, dflag) + int ignore; + int aflag; + int dflag; +{ + sprintf (scratchbuf, "%%st(%d)", rm); + oappend (scratchbuf); + return (0); +} + + +/* capital letters in template are macros */ +static void +putop (template, aflag, dflag) + char *template; + int aflag; + int dflag; +{ + char *p; + + for (p = template; *p; p++) + { + switch (*p) + { + default: + *obufp++ = *p; + break; + case 'C': /* For jcxz/jecxz */ + if (aflag) + *obufp++ = 'e'; + break; + case 'N': + if ((prefixes & PREFIX_FWAIT) == 0) + *obufp++ = 'n'; + break; + case 'S': + /* operand size flag */ + if (dflag) + *obufp++ = 'l'; + else + *obufp++ = 'w'; + break; + case 'W': + /* operand size flag for cwtl, cbtw */ + if (dflag) + *obufp++ = 'w'; + else + *obufp++ = 'b'; + break; + } + } + *obufp = 0; +} + +static void +oappend (s) + char *s; +{ + strcpy (obufp, s); + obufp += strlen (s); + *obufp = 0; +} + +static void +append_prefix () +{ + if (prefixes & PREFIX_CS) + oappend ("%cs:"); + if (prefixes & PREFIX_DS) + oappend ("%ds:"); + if (prefixes & PREFIX_SS) + oappend ("%ss:"); + if (prefixes & PREFIX_ES) + oappend ("%es:"); + if (prefixes & PREFIX_FS) + oappend ("%fs:"); + if (prefixes & PREFIX_GS) + oappend ("%gs:"); +} + +static int +OP_indirE (bytemode, aflag, dflag) + int bytemode; + int aflag; + int dflag; +{ + oappend ("*"); + return OP_E (bytemode, aflag, dflag); +} + +static int +OP_E (bytemode, aflag, dflag) + int bytemode; + int aflag; + int dflag; +{ + 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 v_mode: + if (dflag) + oappend (names32[rm]); + else + oappend (names16[rm]); + break; + default: + oappend (""); + break; + } + return 0; + } + + disp = 0; + append_prefix (); + + if (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 (mod != 0 || base == 5) + { + sprintf (scratchbuf, "0x%x", disp); + oappend (scratchbuf); + } + + if (havebase || (havesib && (index != 4 || scale != 0))) + { + oappend ("("); + if (havebase) + oappend (names32[base]); + if (havesib) + { + if (index != 4) + { + sprintf (scratchbuf, ",%s", names32[index]); + oappend (scratchbuf); + } + sprintf (scratchbuf, ",%d", 1 << scale); + oappend (scratchbuf); + } + oappend (")"); + } + } + 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 (mod != 0 || rm == 6) + { + sprintf (scratchbuf, "0x%x", disp); + oappend (scratchbuf); + } + + if (mod != 0 || rm != 6) + { + oappend ("("); + oappend (index16[rm]); + oappend (")"); + } + } + return 0; +} + +static int +OP_G (bytemode, aflag, dflag) + int bytemode; + int aflag; + int dflag; +{ + 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 (dflag) + oappend (names32[reg]); + else + oappend (names16[reg]); + break; + default: + oappend (""); + break; + } + return (0); +} + +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) + int op; +{ + op_index[op_ad] = op_ad; + op_address[op_ad] = op; +} + +static int +OP_REG (code, aflag, dflag) + int code; + int aflag; + int dflag; +{ + 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 (dflag) + s = names32[code - eAX_reg]; + else + s = names16[code - eAX_reg]; + break; + default: + s = ""; + break; + } + oappend (s); + return (0); +} + +static int +OP_I (bytemode, aflag, dflag) + int bytemode; + int aflag; + int dflag; +{ + int op; + + switch (bytemode) + { + case b_mode: + FETCH_DATA (the_info, codep + 1); + op = *codep++ & 0xff; + break; + case v_mode: + if (dflag) + op = get32 (); + else + op = get16 (); + break; + case w_mode: + op = get16 (); + break; + default: + oappend (""); + return (0); + } + sprintf (scratchbuf, "$0x%x", op); + oappend (scratchbuf); + return (0); +} + +static int +OP_sI (bytemode, aflag, dflag) + int bytemode; + int aflag; + int dflag; +{ + 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 (dflag) + op = get32 (); + else + { + op = get16(); + if ((op & 0x8000) != 0) + op -= 0x10000; + } + break; + case w_mode: + op = get16 (); + if ((op & 0x8000) != 0) + op -= 0x10000; + break; + default: + oappend (""); + return (0); + } + sprintf (scratchbuf, "$0x%x", op); + oappend (scratchbuf); + return (0); +} + +static int +OP_J (bytemode, aflag, dflag) + int bytemode; + int aflag; + int dflag; +{ + 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 (dflag) + disp = get32 (); + else + { + disp = get16 (); + if ((disp & 0x8000) != 0) + disp -= 0x10000; + /* 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; + } + break; + default: + oappend (""); + return (0); + } + disp = (start_pc + codep - start_codep + disp) & mask; + set_op (disp); + sprintf (scratchbuf, "0x%x", disp); + oappend (scratchbuf); + return (0); +} + +/* ARGSUSED */ +static int +OP_SEG (dummy, aflag, dflag) + int dummy; + int aflag; + int dflag; +{ + static char *sreg[] = { + "%es","%cs","%ss","%ds","%fs","%gs","%?","%?", + }; + + oappend (sreg[reg]); + return (0); +} + +static int +OP_DIR (size, aflag, dflag) + int size; + int aflag; + int dflag; +{ + int seg, offset; + + switch (size) + { + case lptr: + if (aflag) + { + offset = get32 (); + seg = get16 (); + } + else + { + offset = get16 (); + seg = get16 (); + } + sprintf (scratchbuf, "0x%x,0x%x", seg, offset); + oappend (scratchbuf); + break; + case v_mode: + if (aflag) + offset = get32 (); + else + { + offset = get16 (); + if ((offset & 0x8000) != 0) + offset -= 0x10000; + } + + offset = start_pc + codep - start_codep + offset; + set_op (offset); + sprintf (scratchbuf, "0x%x", offset); + oappend (scratchbuf); + break; + default: + oappend (""); + break; + } + return (0); +} + +/* ARGSUSED */ +static int +OP_OFF (bytemode, aflag, dflag) + int bytemode; + int aflag; + int dflag; +{ + int off; + + append_prefix (); + + if (aflag) + off = get32 (); + else + off = get16 (); + + sprintf (scratchbuf, "0x%x", off); + oappend (scratchbuf); + return (0); +} + +/* ARGSUSED */ +static int +OP_ESDI (dummy, aflag, dflag) + int dummy; + int aflag; + int dflag; +{ + oappend ("%es:("); + oappend (aflag ? "%edi" : "%di"); + oappend (")"); + return (0); +} + +/* ARGSUSED */ +static int +OP_DSSI (dummy, aflag, dflag) + int dummy; + int aflag; + int dflag; +{ + oappend ("%ds:("); + oappend (aflag ? "%esi" : "%si"); + oappend (")"); + return (0); +} + +#if 0 +/* Not used. */ + +/* ARGSUSED */ +static int +OP_ONE (dummy, aflag, dflag) + int dummy; + int aflag; + int dflag; +{ + oappend ("1"); + return (0); +} + +#endif + +/* ARGSUSED */ +static int +OP_C (dummy, aflag, dflag) + int dummy; + int aflag; + int dflag; +{ + codep++; /* skip mod/rm */ + sprintf (scratchbuf, "%%cr%d", reg); + oappend (scratchbuf); + return (0); +} + +/* ARGSUSED */ +static int +OP_D (dummy, aflag, dflag) + int dummy; + int aflag; + int dflag; +{ + codep++; /* skip mod/rm */ + sprintf (scratchbuf, "%%db%d", reg); + oappend (scratchbuf); + return (0); +} + +/* ARGSUSED */ +static int +OP_T (dummy, aflag, dflag) + int dummy; + int aflag; + int dflag; +{ + codep++; /* skip mod/rm */ + sprintf (scratchbuf, "%%tr%d", reg); + oappend (scratchbuf); + return (0); +} + +static int +OP_rm (bytemode, aflag, dflag) + int bytemode; + int aflag; + int dflag; +{ + switch (bytemode) + { + case d_mode: + oappend (names32[rm]); + break; + case w_mode: + oappend (names16[rm]); + break; + } + return (0); +} + +static int +OP_MMX (bytemode, aflag, dflag) + int bytemode; + int aflag; + int dflag; +{ + sprintf (scratchbuf, "%%mm%d", reg); + oappend (scratchbuf); + return 0; +} + +static int +OP_EM (bytemode, aflag, dflag) + int bytemode; + int aflag; + int dflag; +{ + if (mod != 3) + return OP_E (bytemode, aflag, dflag); + + codep++; + sprintf (scratchbuf, "%%mm%d", rm); + oappend (scratchbuf); + return 0; +} + +static int +OP_MS (bytemode, aflag, dflag) + int bytemode; + int aflag; + int dflag; +{ + ++codep; + sprintf (scratchbuf, "%%mm%d", rm); + oappend (scratchbuf); + return 0; +} diff -urN ikd-ref/arch/i386/kdb/kd_id.c ikd/arch/i386/kdb/kd_id.c --- ikd-ref/arch/i386/kdb/kd_id.c Thu Jan 1 01:00:00 1970 +++ ikd/arch/i386/kdb/kd_id.c Tue Oct 3 20:08:45 2000 @@ -0,0 +1,273 @@ +/* + * Kernel Debugger. + * + * This module parses the instruction disassembly (id) command + * and invokes the disassembler. + * + * Copyright 1999, Silicon Graphics, Inc. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + */ + +#if defined(STANDALONE) +#include +#include +#include +#include +#include "kdb.h" +#include "sakdbsupport.h" +#include "dis-asm.h" +#else +#include +#include +#include +#include +#include +#include "dis-asm.h" +#include "kdbsupport.h" +#endif + +static disassemble_info kdb_di; + +/* ARGSUSED */ +static int +kdbgetsym(bfd_vma addr, struct disassemble_info *dip) +{ + + return 0; +} + +/* ARGSUSED */ +static void +kdbprintintaddr(bfd_vma addr, struct disassemble_info *dip, int flag) +{ + char *sym = kdbnearsym(addr); + int offset = 0; + + if (sym) { + offset = addr - kdbgetsymval(sym); + } + + /* + * Print a symbol name or address as necessary. + */ + if (sym) { + if (offset) + dip->fprintf_func(dip->stream, "%s+0x%x", sym, offset); + else + dip->fprintf_func(dip->stream, "%s", sym); + } else { + dip->fprintf_func(dip->stream, "0x%x", addr); + } + + if (flag) + dip->fprintf_func(dip->stream, ": "); +} + +static void +kdbprintaddr(bfd_vma addr, struct disassemble_info *dip) +{ + kdbprintintaddr(addr, dip, 0); +} + +/* ARGSUSED */ +static int +kdbgetidmem(bfd_vma addr, bfd_byte *buf, int length, struct 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; i + * + * 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) +{ + unsigned long pc; + int icount; + int diag; + int i; + char * mode; + int nextarg; + long offset = 0; + static unsigned long lastpc=0; + + 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; + + /* + * Number of lines to display + */ + diag = kdbgetintenv("IDCOUNT", &icount); + if (diag) + return diag; + + mode = kdbgetenv("IDMODE"); + if (mode) { + if (strcmp(mode, "x86") == 0) { + kdb_di.mach = bfd_mach_i386_i386; + } else if (strcmp(mode, "8086") == 0) { + kdb_di.mach = bfd_mach_i386_i8086; + } else { + return KDB_BADMODE; + } + } + + for(i=0; i +#include +#include +#include +#include +#if defined(__SMP__) +#include +#endif +#include +#include +#include "kdbsupport.h" + +int kdb_active = 0; +int kdb_flags = 0; +int kdb_nextline = 1; +int kdb_new_cpu = -1; + +kdb_jmp_buf kdbjmpbuf; + + /* + * Describe the command table. + */ +typedef struct _kdbtab { + char *cmd_name; /* Command name */ + kdb_func 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; + + /* + * Provide space for KDB_MAX_COMMANDS commands. + */ +#define KDB_MAX_COMMANDS 100 + +static kdbtab_t kdb_commands[KDB_MAX_COMMANDS]; + + /* + * Error messages. XXX - use a macro for this... + */ +static char kdb_notfound[] = "Command Not Found"; +static char kdb_argcount[] = "Improper argument count, see usage."; +static char kdb_badwidth[] = "Illegal value for BYTESPERWORD use 1, 2 or 4"; +static char kdb_badradix[] = "Illegal value for RADIX use 8, 10 or 16"; +static char kdb_notenv[] = "Cannot find environment variable"; +static char kdb_noenvvalue[] = "Environment variable should have value"; +static char kdb_notimp[] = "Command not implemented"; +static char kdb_envfull[] = "Environment full"; +static char kdb_envbuffull[] = "Environment buffer full"; +static char kdb_toomanybpt[] = "Too many breakpoints defined"; +static char kdb_toomanydbregs[] = "More breakpoints than db registers defined"; +static char kdb_dupbpt[] = "Duplicate breakpoint address"; +static char kdb_bptnotfound[] = "Breakpoint not found"; +static char kdb_badmode[] = "IDMODE should be x86 or 8086"; +static char kdb_badint[] = "Illegal numeric value"; +static char kdb_invaddrfmt[] = "Invalid symbolic address format"; +static char kdb_badreg[] = "Invalid register name"; +static char kdb_badcpunum[] = "Invalid cpu number"; +static char kdb_badlength[] = "Invalid length field"; +static char kdb_nobp[] = "No Breakpoint exists"; + +static char *kdb_messages[] = { + (char *)0, + kdb_notfound, + (char *)0, + kdb_argcount, + kdb_badwidth, + kdb_badradix, + kdb_notenv, + kdb_noenvvalue, + kdb_notimp, + kdb_envfull, + kdb_envbuffull, + kdb_toomanybpt, + kdb_toomanydbregs, + kdb_dupbpt, + kdb_bptnotfound, + kdb_badmode, + kdb_badint, + kdb_invaddrfmt, + kdb_badreg, + (char *)0, /* KDB_CPUSWITCH */ + kdb_badcpunum, + kdb_badlength, + kdb_nobp, + (char *)0 +}; +static const int __nkdb_err = sizeof(kdb_messages) / sizeof(char *); + + +/* + * 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). + */ + +#if defined(CONFIG_SMP) +static char prompt_str[] = "PROMPT=[%d]kdb> "; +static char more_str[] = "MOREPROMPT=[%d]more> "; +#else +static char prompt_str[] = "PROMPT=kdb> "; +static char more_str[] = "MOREPROMPT=more> "; +#endif +static char radix_str[] = "RADIX=16"; +static char lines_str[] = "LINES=24"; +static char columns_str[] = "COLUMNS=80"; +static char mdcount_str[] = "MDCOUNT=8"; /* lines of md output */ +static char idcount_str[] = "IDCOUNT=16"; /* lines of id output */ +static char bperword_str[] = "BYTESPERWORD=4"; /* Word size for md output */ +static char idmode_str[] = "IDMODE=x86"; /* 32-bit mode */ +static char btargs_str[] = "BTARGS=5"; /* 5 possible args in bt */ +static char sscount_str[] = "SSCOUNT=20"; /* lines of ssb output */ + +static char *__env[] = { + bperword_str, + columns_str, + idcount_str, + lines_str, + mdcount_str, + prompt_str, + radix_str, + idmode_str, + btargs_str, + sscount_str, + more_str, + (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 = 0; + 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[] + * + * 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, + unsigned long *value, long *offset, + char **name, struct pt_regs *regs) +{ + unsigned long addr; + long off = 0; + int positive; + int diag; + char *symname; + char symbol = '\0'; + char *cp; + + /* + * 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 = kdbgetregcontents(&symname[1], regs, &addr); + if (diag) + return diag; + } else { + addr = kdbgetsymval(symname); + if (addr == 0) { + diag = kdbgetularg(argv[*nextarg], &addr); + if (diag) + return diag; + } + } + + symname = kdbnearsym(addr); + + (*nextarg)++; + + if (name) + *name = symname; + if (value) + *value = addr; + if (offset && name && *name) + *offset = addr - kdbgetsymval(*name); + + 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) +{ + if (diag >= 0) { + kdb_printf("no error detected\n"); + return; + } + diag = -diag; + if (diag >= __nkdb_err) { + kdb_printf("Illegal diag %d\n", -diag); + return; + } + kdb_printf("diag: %d: %s\n", diag, kdb_messages[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, struct pt_regs *regs) +{ + 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 */ + } + + 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) { + return (*tp->cmd_func)(argc-1, + (const char**)argv, + (const char**)__env, + regs); + } + + /* + * 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. + */ + { + unsigned long value; + char *name = NULL; + long offset; + int nextarg = 0; + + if (kdbgetaddrarg(0, (const char **)argv, &nextarg, + &value, &offset, &name, regs)) { + return KDB_NOTFOUND; + } + + kdb_printf("%s = 0x%8.8x ", argv[0], value); + if (name) { + kdb_printf("(%s+0x%lx)", name, offset); + } + kdb_printf("\n"); + return 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_ENTER + * (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 #. + * + * Inputs: + * reason The reason KDB was invoked + * error The hardware-defined error code + * regs The registers at time of fault/breakpoint + * Outputs: + * none + * 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. + */ + +int +kdb(int reason, int error, struct pt_regs *regs) +{ + char cmdbuf[255]; + char *cmd; + int diag; + unsigned long flags; + struct pt_regs func_regs; + + kdb_new_cpu = -1; + + /* + * Remove the breakpoints to prevent double-faults + * if kdb happens to use a function where a breakpoint + * has been enabled. + */ + + kdb_bp_remove(); + + if (reason != KDB_REASON_DEBUG) { + kdb_printf("Entering kdb "); +#if defined(__SMP__) + kdb_printf("on processor %d ", smp_processor_id()); +#endif + } + + switch (reason) { + case KDB_REASON_DEBUG: + /* + * If re-entering kdb after a single step + * command, don't print the message. + */ + diag = kdb_db_trap(regs); + if (diag == 0) { + kdb_printf("Entering kdb "); +#if defined(__SMP__) + kdb_printf("on processor %d ", smp_processor_id()); +#endif + } else if (diag == 2) { + /* + * in middle of ssb command. Just return. + */ + return 0; + } + break; + case KDB_REASON_FAULT: + break; + case KDB_REASON_INT: + kdb_printf("due to KDB_ENTER() call\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_ENTER: + kdb_printf("due to function call\n"); + regs = &func_regs; + regs->xcs = 0; +#if defined(CONFIG_KDB_FRAMEPTR) + asm volatile("movl %%ebp,%0":"=m" (*(int *)®s->ebp)); +#endif + asm volatile("movl %%esp,%0":"=m" (*(int *)®s->esp)); + regs->eip = (long) &kdb; /* for traceback. */ + break; + case KDB_REASON_PANIC: + kdb_printf("due to panic @ 0x%8.8x\n", regs->eip); + kdbdumpregs(regs, NULL, NULL); + break; + case KDB_REASON_BREAK: + kdb_printf("due to Breakpoint @ 0x%8.8x\n", regs->eip); + break; + default: + break; + } + +#if defined(__SMP__) + /* + * If SMP, stop other processors + */ + if (smp_num_cpus > 1) { + /* + * Stop all other processors + */ + smp_kdb_stop(1); + } +#endif /* __SMP__ */ + + /* + * Disable interrupts during kdb command processing + */ + __save_flags(flags); + __cli(); + + while (1) { + /* + * Initialize pager context. + */ + kdb_nextline = 1; + + /* + * Use kdb_setjmp/kdb_longjmp to break out of + * the pager early. + */ + if (kdb_setjmp(&kdbjmpbuf)) { + /* + * Command aborted (usually in pager) + */ + + /* + * XXX - need to abort a SSB ? + */ + continue; + } + + /* + * Fetch command from keyboard + */ + cmd = kbd_getstr(cmdbuf, sizeof(cmdbuf), kdbgetenv("PROMPT")); + + diag = kdb_parse(cmd, regs); + if (diag == KDB_NOTFOUND) { + kdb_printf("Unknown kdb command: '%s'\n", cmd); + diag = 0; + } + if ((diag == KDB_GO) + || (diag == KDB_CPUSWITCH)) + break; /* Go or cpu switch command */ + + if (diag) + kdb_cmderror(diag); + } + + /* + * Set up debug registers. + */ + kdb_bp_install(); + +#if defined(__SMP__) + if ((diag == KDB_CPUSWITCH) + && (kdb_new_cpu != -1)) { + /* + * Leaving the other CPU's at the barrier, except the + * one we are switching to, we'll send ourselves a + * kdb IPI before allowing interrupts so it will get + * caught ASAP and get this CPU back waiting at the barrier. + */ + + smp_kdb_stop(0); /* Stop ourself */ + + /* + * let the new cpu go. + */ + clear_bit(kdb_new_cpu, &smp_kdb_wait); + } else { + /* + * Let the other processors continue. + */ + smp_kdb_wait = 0; + } +#endif + + kdb_flags &= ~(KDB_FLAG_SUPRESS|KDB_FLAG_FAULT); + + __restore_flags(flags); + + + return 0; +} + +/* + * kdb_md + * + * This function implements the 'md' and 'mds' commands. + * + * md|mds [ [ []]] + * + * 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; + unsigned long addr; + unsigned long word; + long offset = 0; + int diag; + int nextarg; + static unsigned long lastaddr = 0; + static unsigned long lastcount = 0; + static unsigned long lastradix = 0; + char lastbuf[50]; + int symbolic = 0; + + /* + * Defaults in case the relevent environment variables are unset + */ + radix = 16; + count = 8; + width = 4; + + if (argc == 0) { + if (lastaddr == 0) + return KDB_ARGCOUNT; + sprintf(lastbuf, "0x%lx", lastaddr); + argv[1] = lastbuf; + argc = 1; + count = lastcount; + radix = lastradix; + } else { + unsigned long 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; + } + + diag = kdbgetintenv("BYTESPERWORD", &width); + + if (strcmp(argv[0], "mds") == 0) { + symbolic = 1; + width = 4; + } + + switch (width) { + case 4: + sprintf(fmtstr, "%%8.8%c ", fmtchar); + break; + case 2: + sprintf(fmtstr, "%%4.4%c ", fmtchar); + break; + case 1: + sprintf(fmtstr, "%%2.2%c ", fmtchar); + break; + default: + return KDB_BADWIDTH; + } + + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + /* 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 = kdbgetword(addr, sizeof(word)); + if (kdb_flags & KDB_FLAG_SUPRESS) { + kdb_flags &= ~KDB_FLAG_SUPRESS; + return 0; + } + + *(unsigned long *)(addr) = contents; + + kdb_printf("0x%x = 0x%x\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: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_go(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + unsigned long addr; + int diag; + int nextarg; + long offset; + + if (argc == 1) { + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + regs->eip = addr; + } else if (argc) + return KDB_ARGCOUNT; + + return KDB_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 kdbdumpregs(regs, NULL, NULL); + } + + if (argc > 2) { + return KDB_ARGCOUNT; + } + + return kdbdumpregs(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, struct pt_regs *regs) +{ + int diag; + int ind = 0; + unsigned long 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 = kdbsetregcontents(&argv[1][ind], regs, contents); + if (diag) + return diag; + + return 0; +} + +/* + * 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) +{ + + return KDB_NOTIMP; +} + +/* + * 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; +} + +/* + * 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]); + } + } + + 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; + + /* + * 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; +} + +#if defined(__SMP__) +/* + * 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: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +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) + || !test_bit(cpunum, &cpu_online_map)) + return KDB_BADCPUNUM; + + kdb_new_cpu = cpunum; + + /* + * Switch to other cpu + */ + return KDB_CPUSWITCH; +} +#endif /* __SMP__ */ + +/* + * 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 lcpu Tss Command\n"); + for_each_task(p) { + kdb_printf("0x%8.8x %10.10d %10.10d %4.4d %4.4d 0x%8.8x %s\n", + p, p->pid, p->p_pptr->pid, p->processor, + p->last_processor, + &p->tss, + 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; + unsigned long addr; + long offset = 0; + unsigned long 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 0x%lx\n", argv[3], va); + diag = kdb_parse(buf, regs); + if (diag) + return diag; + + addr = va + linkoffset; + va = kdbgetword(addr, sizeof(va)); + if (kdb_flags & KDB_FLAG_SUPRESS) { + kdb_flags &= ~KDB_FLAG_SUPRESS; + return 0; + } + } + + 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 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: + * + */ + +void +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("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("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("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); + 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); +#if defined(__SMP__) + kdb_register("cpu", kdb_cpu, "","Switch to new cpu", 0); +#endif /* __SMP__ */ + kdb_register("ps", kdb_ps, "", "Display active task list", 0); + kdb_register("reboot", kdb_reboot, "", "Reboot the machine immediately", 0); +} + diff -urN ikd-ref/arch/i386/kdb/kdb_bp.c ikd/arch/i386/kdb/kdb_bp.c --- ikd-ref/arch/i386/kdb/kdb_bp.c Thu Jan 1 01:00:00 1970 +++ ikd/arch/i386/kdb/kdb_bp.c Tue Oct 3 20:08:45 2000 @@ -0,0 +1,707 @@ +/* + * Kernel Debugger Breakpoint Handler + * + * Copyright 1999, Silicon Graphics, Inc. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + */ + +#include +#include +#include +#if defined(__SMP__) +#include +#include +#endif +#include +#include "kdbsupport.h" + +/* + * Table of breakpoints + */ +#define KDB_MAXBPT 16 +static kdb_bp_t breakpoints[KDB_MAXBPT]; + +static char *rwtypes[] = {"Instruction", "Data Write", "I/O", "Data Access"}; + +/* + * kdb_bp_install + * + * Install breakpoints prior to returning from the kernel debugger. + * This allows the breakpoints to be set upon functions that are + * used internally by kdb, such as printk(). + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdb_bp_install(void) +{ + int i; + + for(i=0; ieip); + if (op1 == 0x0f) + op2 = *(unsigned char *)(ef->eip+1); + 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_flags &= ~KDB_FLAG_SSB; + } else { + kdb_id1(ef->eip); + rv = 2; /* Indicate ssb - dismiss immediately */ + } + } else { + /* + * Print current insn + */ + kdb_printf("SS trap at 0x%x\n", ef->eip); + kdb_id1(ef->eip); + } + + if (rv != 2) + 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; ieip); + } + + goto handled; + } + } + +unknown: + kdb_printf("Unknown breakpoint. Should forward. \n"); + +handled: + + /* + * Clear the pending exceptions. + */ + kdb_putdr6(0); + + return rv; +} + +/* + * kdb_printbp + * + * Internal function to format and print a breakpoint entry. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +static void +kdb_printbp(kdb_bp_t *bp, int i) +{ + char *symname; + long offset; + + kdb_printf("%s ", rwtypes[bp->bp_mode]); + + kdb_printf("BP #%d at 0x%x ", + i, bp->bp_addr); + symname = kdbnearsym(bp->bp_addr); + if (symname){ + kdb_printf("(%s", symname); + + offset = bp->bp_addr - kdbgetsymval(symname); + if (offset) { + if (offset > 0) + kdb_printf("+0x%x", offset); + else + kdb_printf("-0x%x", offset); + } + kdb_printf(") "); + } + + if (bp->bp_enabled) { + kdb_printf("\n is enabled in dr%d", bp->bp_reg); + if (bp->bp_global) + kdb_printf(" globally"); + else + kdb_printf(" on cpu %d", bp->bp_cpu); + + if (bp->bp_mode != 0) { + kdb_printf(" for %d bytes", bp->bp_length + 1); + } + } else { + kdb_printf("\n is disabled"); + } + + kdb_printf("\n"); +} + +/* + * kdb_bp + * + * Handle the bp, and bpa commands. + * + * [bp|bpa] [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: + */ + +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; + k_machreg_t addr; + char *symname = NULL; + long offset = 0ul; + int nextarg; + unsigned long mode, length; +#if defined(__SMP__) + int global; +#endif + + if (argc == 0) { + /* + * Display breakpoint table + */ + for(i=0,bp=breakpoints; ibp_free) continue; + + kdb_printbp(bp, i); + } + + return 0; + } + +#if defined(__SMP__) + global = (strcmp(argv[0], "bpa") == 0); +#endif + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, &symname, regs); + if (diag) + return diag; + + mode = 0; /* Default to instruction breakpoint */ + length = 0; /* Length must be zero for insn bp */ + if ((argc + 1) != nextarg) { + if (strnicmp(argv[nextarg], "datar", sizeof("datar")) == 0) { + mode = 3; + } else if (strnicmp(argv[nextarg], "dataw", sizeof("dataw")) == 0) { + mode = 1; + } else if (strnicmp(argv[nextarg], "io", sizeof("io")) == 0) { + mode = 2; + } else { + return KDB_ARGCOUNT; + } + + length = 3; /* Default to 4 byte */ + + nextarg++; + + if ((argc + 1) != nextarg) { + diag = kdbgetularg((char *)argv[nextarg], &length); + if (diag) + return diag; + + if ((length > 4) || (length == 3)) + return KDB_BADLENGTH; + + length--; /* Normalize for debug register */ + nextarg++; + } + + if ((argc + 1) != nextarg) + return KDB_ARGCOUNT; + } + + /* + * Allocate a new bp structure + */ + free = same = KDB_MAXBPT; + for(i=0; ibp_addr = addr; + bp->bp_mode = mode; + bp->bp_length = length; + + /* + * Find a free register + */ + for(i=0; ibp_reg = i; + bp->bp_free = 0; + bp->bp_enabled = 1; +#if defined(__SMP__) + if (global) + bp->bp_global = 1; + else + bp->bp_cpu = smp_processor_id(); +#endif + + kdb_printbp(bp, free); + + break; + } + } + /* + * XXX if not enough regs, fail. Eventually replace + * first byte of instruction with int03. + */ + if (i==KDB_DBREGS) + return KDB_TOOMANYDBREGS; + + 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) +{ + k_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 = &breakpoints[addr]; + lowbp = highbp = addr; + highbp++; + } else { + for(i=0; ibp_free) + continue; + + done++; + + switch (cmd) { + case KDBCMD_BC: + kdbremovedbreg(bp->bp_reg); + dbregs[bp->bp_reg] = 0xffffffff; + kdb_printf("Breakpoint %d at 0x%x in dr%d cleared\n", + i, bp->bp_addr, bp->bp_reg); + bp->bp_free = 1; + bp->bp_addr = 0; + break; + case KDBCMD_BE: + { + int r; + + for(r=0; rbp_reg = r; + bp->bp_enabled = 1; + kdb_printf("Breakpoint %d at 0x%x in dr%d enabled\n", + i, bp->bp_addr, bp->bp_reg); + break; + } + } + if (r == KDB_DBREGS) + return KDB_TOOMANYDBREGS; + + break; + } + case KDBCMD_BD: + kdbremovedbreg(bp->bp_reg); + dbregs[bp->bp_reg] = 0xffffffff; + bp->bp_enabled = 0; + kdb_printf("Breakpoint %d at 0x%x in dr%d disabled\n", + i, bp->bp_addr, bp->bp_reg); + break; + } + } + + return (!done)?KDB_BPTNOTFOUND:0; +} + +/* + * kdb_initbptab + * + * Initialize the breakpoint table. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdb_initbptab(void) +{ + int i; + + /* + * First time initialization. + */ + for (i=0; i] + * ssb + * + * Parameters: + * argc Argument count + * argv Argument vector + * envp Environment vector + * regs Registers at time of entry to kernel debugger + * Outputs: + * None. + * Returns: + * 0 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, struct pt_regs *regs) +{ + 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(regs->eip); + + /* + * Set trace flag and go. + */ + regs->eflags |= EF_TF; + + if (ssb) + kdb_flags |= KDB_FLAG_SSB; + + return KDB_GO; +} diff -urN ikd-ref/arch/i386/kdb/kdb_bt.c ikd/arch/i386/kdb/kdb_bt.c --- ikd-ref/arch/i386/kdb/kdb_bt.c Thu Jan 1 01:00:00 1970 +++ ikd/arch/i386/kdb/kdb_bt.c Tue Oct 3 20:08:45 2000 @@ -0,0 +1,389 @@ + /* + * Minimalist Kernel Debugger + * + * Machine dependent stack traceback code. + * + * Copyright 1999, Silicon Graphics, Inc. + * + * Written by Scott Lurndal at Silicon Graphics, March 1999. + */ + +#include +#include +#include +#include +#include +#include +#include "kdbsupport.h" + + +#if defined(CONFIG_KDB_FRAMEPTR) +/* + * kdb_prologue + * + * This function analyzes a gcc-generated function prototype + * when frame pointers are enabled to determine the amount of + * automatic storage and register save storage is used on the + * stack of the target function. + * Parameters: + * eip Address of function to analyze + * Outputs: + * *nauto # bytes of automatic storage + * *nsave # of saved registers + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + * A prologue generally looks like: + * + * pushl %ebp (All functions) + * movl %esp, %ebp (All functions) + * subl $auto, %esp [some functions] + * pushl reg [some functions] + * pushl reg [some functions] + */ + +void +kdb_prologue(unsigned long eip, unsigned long *nauto, unsigned long *nsave) +{ + size_t insn_size = sizeof(unsigned char); + + *nauto = 0; + *nsave = 0; + + if (eip == 0) + return; + + if ((kdbgetword(eip, insn_size) != 0x55) /* pushl %ebp */ + || (kdbgetword(eip+1, insn_size) != 0x89)){/* movl esp, ebp */ + /* + * Unknown prologue type. + */ + return; + } + + if (kdbgetword(eip+3, insn_size) == 0x83) { + *nauto = kdbgetword(eip+5, sizeof(unsigned char)); + eip += 6; + } else if (kdbgetword(eip+3, insn_size) == 0x81) { + *nauto = kdbgetword(eip+5, sizeof(unsigned long)); + eip += 9; + } else + eip += 3; + + + while ((kdbgetword(eip, insn_size)&0xf8) == 0x50) + (*nsave)++, eip++; +} +#endif + +/* + * 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 + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Doing this without a frame pointer is _hard_. some simple + * things are done here, but we usually don't find more than the + * first couple of frames yet. More coming in this area. + * + * mds comes in handy when examining the stack to do a manual + * traceback. + */ + +int +kdb_bt(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int done = 0; +#if !defined(CONFIG_KDB_FRAMEPTR) + unsigned long sp; +#endif + unsigned long base, limit, esp, ebp, eip; + unsigned long start = 0; /* Start address of current function */ + unsigned long addr; + long offset = 0; + int nextarg; + int argcount=5; + char *name; + int diag; + struct pt_regs taskregs; + struct frame { + unsigned long ebp; + unsigned long eip; + } old; + unsigned long stackbase = (unsigned long)current; + + /* + * Determine how many possible arguments to print. + */ + diag = kdbgetintenv("BTARGS", &argcount); + + if (strcmp(argv[0], "btp") == 0){ + struct task_struct *p; + int pid; + + diag = kdbgetularg((char *)argv[1], (unsigned long*)&pid); + if (diag) + return diag; + + taskregs.eax = 1; + for_each_task(p) { + if (p->pid == pid) { + taskregs.eip = p->tss.eip; + taskregs.esp = p->tss.esp; + taskregs.ebp = p->tss.ebp; + /* + * 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) + */ + if (taskregs.ebp == 0) { + taskregs.ebp = + *(unsigned long *)(taskregs.esp); + } + + taskregs.eax = 0; + stackbase = (unsigned long)p; + break; + } + } + + if (taskregs.eax == 1) { + kdb_printf("No process with pid == %d found\n", + pid); + return 0; + } + regs = &taskregs; + } else { + if (argc) { + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + /* + * We assume the supplied address is points to an + * EIP value on the stack, and the word pushed above + * it is an EBP value. Start as if we were in the + * middle of that frame. + */ + taskregs.eip = kdbgetword(addr, sizeof(unsigned long)); + taskregs.ebp = kdbgetword(addr-4, sizeof(unsigned long)); + taskregs.ebp = kdbgetword(taskregs.ebp, sizeof(unsigned long)); + regs = &taskregs; + } + } + + name = kdbnearsym(regs->eip); + if (name) { + start = kdbgetsymval(name); + } else { + kdb_printf("Cannot determine function for eip = 0x%x\n", + regs->eip); + return 0; + } +#if !defined(CONFIG_KDB_FRAMEPTR) + /* + * starting with %esp value, track the stack back to + * the end. + */ + + sp = (unsigned long) (1+regs); /* Point past exception frame */ + sp -= 8; /* Adjust to encompass return addr */ + if (regs->xcs & 3) { + /* User space? */ + sp = regs->esp; + } + sp -= 8; /* Adjust to encompass return addr */ + base = sp & ~0x1fff; /* XXX - use stack size constant */ + + + kdb_printf(" ESP EIP Function(args)\n"); + kdb_printf("0x%x 0x%x %s ()\n", + sp, regs->eip, name); + + while (!done) { + extern char _text, _etext; + extern char __init_begin, __init_end; + unsigned long word; + + word = *(unsigned long *)(sp); + if ( ((word >= (unsigned long)&_text) + && (word <= (unsigned long)&_etext)) + || ((word >= (unsigned long)&__init_begin) + && (word <= (unsigned long)&__init_end))) { + /* text address */ + +#if defined(NOTNOW) + kdb_printf("word %x sp %x char %x off %x\n", + word, sp, *(unsigned char *)(word - 5), + *(signed long *)(word - 4)); +#endif + /* + * If the instruction 5 bytes before the + * return instruction is a call, treat this + * entry as a probable activation record + */ + if (*(unsigned char *)(word - 5) == 0xe8) { + signed long val = *(signed long *)(word - 4); + int i; + + /* + * Is this a real activation record? + */ + /* + * Of course, this misses all the + * indirect calls, etc. XXX XXX + */ + if ((val + word) == start) { + kdb_printf("0x%x 0x%x ", + sp, word); + name = kdbnearsym(word); + if (name) { + kdb_printf("%s (", name); + } + for(i=0; i= base+0x2000) /* XXX - use stack size constant */ + done = 1; + } +#else + kdb_printf(" EBP EIP Function(args)\n"); + + base = stackbase; + + esp = regs->esp; + ebp = regs->ebp; + eip = regs->eip; + + limit = base + 0x2000; /* stack limit */ + +#if 0 + printk("stackbase = 0x%lx base = 0x%lx limit = 0x%lx ebp = 0x%lx esp = 0x%lx eip = 0x%lx\n", + stackbase, base, limit, ebp, esp, eip); +#endif + + if ((eip == start) /* Beginning of function */ + || (eip == start+1)) { /* Right after pushl %ebp */ + /* + * If at start of function, create a dummy frame + */ + old.ebp = ebp; + if (kdb_flags & KDB_FLAG_FAULT) { + if ((regs->xcs&0xffff) == 0x10) { + /* + * For instruction breakpoint on a target of a + * call instruction, we must fetch the caller's + * eip shall we say, differently. + */ + old.eip = regs->esp; /* Don't Ask */ + } else { + old.eip = *(unsigned long *)(regs+1); + } + } else { + old.eip = (eip == start) + ?kdbgetword(esp, sizeof(unsigned long)) + :kdbgetword(esp+4, sizeof(unsigned long)); + } + ebp = (unsigned long)&old; + } + + /* + * While the ebp remains within the stack, continue to + * print stack entries. + * + * XXX - this code depends on the fact that kernel + * stacks are aligned on address boundaries + * congruent to 0 modulo 8192 and are 8192 bytes + * in length and are part of the 8k bytes based + * at the process structure (e.g. current). + * + */ + + while (!done) { + unsigned long nebp, neip; + char *newname; + + kdb_printf("0x%x 0x%x ", ebp, eip); + kdb_printf("%s", name); + + if (ebp < PAGE_OFFSET) { + done++; + kdb_printf("\n"); + continue; + } + + nebp = kdbgetword(ebp, sizeof(unsigned long)); + neip = kdbgetword(ebp+4, sizeof(unsigned long)); + + if (eip != start) { + kdb_printf("+0x%x", eip - start); + } + + /* + * Get frame for caller to determine the frame size + * and argument count. + */ + newname = kdbnearsym(neip); + if (newname) { + int i; + unsigned long nauto, nsave, nargs; + + start = kdbgetsymval(newname); + name = newname; + + kdb_printf("( "); + + kdb_prologue(kdbgetsymval(newname), &nauto, &nsave); + + nargs = (nebp - ebp - 8 - nauto - (nsave * 4))/4; + if (nargs > argcount) + nargs = argcount; + for(i=0; i buffer) { + --cp, bufsize++; + printk("%c %c", 0x08, 0x08); + } + continue; + } + serial_out(kdb_port, UART_TX, ch); + if (ch == 13) { /* CR */ + *cp++ = '\n'; + *cp++ = '\0'; + serial_out(kdb_port, UART_TX, 10); + return(buffer); + } + /* + * Discard excess characters + */ + if (bufsize > 0) { + *cp++ = ch; + bufsize--; + } + } + while (((status = serial_inp(kdb_port, UART_LSR)) + & UART_LSR_DR) == 0); + } + } + + while (1) { + + /* + * Wait for a valid scancode + */ + + while ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0) + ; + + /* + * Fetch the scancode + */ + scancode = inb(KBD_DATA_REG); + scanstatus = inb(KBD_STATUS_REG); + + /* + * Ignore mouse events. + */ + if (scanstatus & KBD_STAT_MOUSE_OBF) + continue; + + /* + * 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; + } + continue; + } + + if ((scancode&0x7f) == 0x1d) { + /* + * Left ctrl key + */ + if ((scancode & 0x80) == 0) { + ctrl_key = 1; + } else { + ctrl_key = 0; + } + continue; + } + + if ((scancode & 0x80) != 0) + continue; + + scancode &= 0x7f; + + /* + * Translate scancode + */ + + if (scancode == 0x3a) { + /* + * Toggle caps lock + */ + shift_lock ^= 1; + leds ^= 0x4; /* toggle caps lock led */ + + kdb_kbdsetled(leds); + continue; + } + + if (scancode == 0x0e) { + /* + * Backspace + */ + if (cp > buffer) { + --cp, bufsize++; + + /* + * XXX - erase character on screen + */ + printk("%c %c", 0x08, 0x08); + } + continue; + } + + if (scancode == 0xe0) { + continue; + } + + /* + * 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) { + 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; + printk("Unknown state/scancode (%d)\n", scancode); + } + + if ((scancode & 0x7f) == 0x1c) { + /* + * enter key. All done. + */ + printk("\n"); + break; + } + + /* + * echo the character. + */ + printk("%c", keychar&0xff); + + if (bufsize) { + --bufsize; + *cp++ = keychar&0xff; + } else { + printk("buffer overflow\n"); + break; + } + + } + + *cp++ = '\n'; /* White space for parser */ + *cp++ = '\0'; /* String termination */ + +#if defined(NOTNOW) + cp = buffer; + while (*cp) { + printk("char 0x%x\n", *cp++); + } +#endif + + return buffer; +} diff -urN ikd-ref/arch/i386/kdb/kdbsupport.c ikd/arch/i386/kdb/kdbsupport.c --- ikd-ref/arch/i386/kdb/kdbsupport.c Thu Jan 1 01:00:00 1970 +++ ikd/arch/i386/kdb/kdbsupport.c Tue Oct 3 20:08:45 2000 @@ -0,0 +1,936 @@ +/* + * Kernel Debugger Breakpoint Handler + * + * Copyright 1999, Silicon Graphics, Inc. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kdbsupport.h" + +k_machreg_t dbregs[KDB_DBREGS]; + +void +kdb_init(void) +{ + extern void kdb_inittab(void); + + kdb_inittab(); + kdb_initbptab(); + kdb_disinit(); + kdb_printf("kdb version %d.%d by Scott Lurndal. "\ + "Copyright SGI, All Rights Reserved\n", + KDB_MAJOR_VERSION, KDB_MINOR_VERSION); +} + +/* + * kdbprintf + * kdbgetword + * kdb_getstr + */ + +char * +kbd_getstr(char *buffer, size_t bufsize, char *prompt) +{ + extern char* kdb_getscancode(char *, size_t); + +#if defined(CONFIG_SMP) + kdb_printf(prompt, smp_processor_id()); +#else + kdb_printf("%s", prompt); +#endif + + return kdb_getscancode(buffer, bufsize); + +} + +int +kdb_printf(const char *fmt, ...) +{ + char buffer[256]; + va_list ap; + int diag; + int linecount; + + diag = kdbgetintenv("LINES", &linecount); + if (diag) + linecount = 22; + + va_start(ap, fmt); + vsprintf(buffer, fmt, ap); + va_end(ap); + + printk("%s", buffer); + + if (strchr(buffer, '\n') != NULL) { + kdb_nextline++; + } + + if (kdb_nextline == linecount) { + char buf1[16]; + char buf2[32]; + extern char* kdb_getscancode(char *, size_t); + char *moreprompt; + + /* + * 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 + + printk(moreprompt); + (void) kdb_getscancode(buf1, sizeof(buf1)); + + kdb_nextline = 1; + + if ((buf1[0] == 'q') + || (buf1[0] == 'Q')) { + kdb_longjmp(&kdbjmpbuf, 1); + } + } + + return 0; +} + +unsigned long +kdbgetword(unsigned long addr, int width) +{ + /* + * 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; + unsigned long ulval; + + switch (width) { + case 4: + { unsigned long *lp; + + lp = (unsigned long *) addr; + diag = get_user(ulval, lp); + break; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *) addr; + diag = get_user(ulval, sp); + break; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *) addr; + diag = get_user(ulval, cp); + break; + } + default: + printk("kdbgetword: Bad width\n"); + return 0L; + } + + if (diag) { + if ((kdb_flags & KDB_FLAG_SUPRESS) == 0) { + printk("kdb: Bad user address 0x%lx\n", addr); + kdb_flags |= KDB_FLAG_SUPRESS; + } + return 0L; + } + kdb_flags &= ~KDB_FLAG_SUPRESS; + return ulval; + } + + if (addr > (unsigned long)high_memory) { + extern int kdb_vmlist_check(unsigned long, unsigned long); + + 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_flags & KDB_FLAG_SUPRESS) == 0) { + printk("kdb: Bad kernel address 0x%lx\n", addr); + kdb_flags |= KDB_FLAG_SUPRESS; + } + return 0L; + } + } + + /* + * A good address. Reset error flag. + */ + kdb_flags &= ~KDB_FLAG_SUPRESS; + + 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; + } + } + + printk("kdbgetword: Bad width\n"); + return 0L; +} + +int +kdbinstalltrap(int type, handler_t newh, handler_t *oldh) +{ + /* + * Usurp INTn. XXX - TBD. + */ + + return 0; +} + +int +kdbinstalldbreg(kdb_bp_t *bp) +{ + k_machreg_t dr7; + + dr7 = kdb_getdr7(); + + kdb_putdr(bp->bp_reg, bp->bp_addr); + + dr7 |= DR7_GE; + + switch (bp->bp_reg){ + case 0: + DR7_RW0SET(dr7,bp->bp_mode); + DR7_LEN0SET(dr7,bp->bp_length); + DR7_G0SET(dr7); + break; + case 1: + DR7_RW1SET(dr7,bp->bp_mode); + DR7_LEN1SET(dr7,bp->bp_length); + DR7_G1SET(dr7); + break; + case 2: + DR7_RW2SET(dr7,bp->bp_mode); + DR7_LEN2SET(dr7,bp->bp_length); + DR7_G2SET(dr7); + break; + case 3: + DR7_RW3SET(dr7,bp->bp_mode); + DR7_LEN3SET(dr7,bp->bp_length); + DR7_G3SET(dr7); + break; + default: + kdb_printf("Bad debug register!! %d\n", bp->bp_reg); + break; + } + + kdb_putdr7(dr7); + return 0; +} + +void +kdbremovedbreg(int regnum) +{ + k_machreg_t dr7; + + 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("Bad debug register!! %d\n", regnum); + break; + } + + kdb_putdr7(dr7); +} + +k_machreg_t +kdb_getdr6(void) +{ + return kdb_getdr(6); +} + +k_machreg_t +kdb_getdr7(void) +{ + return kdb_getdr(7); +} + +k_machreg_t +kdb_getdr(int regnum) +{ + k_machreg_t contents = 0; +#if defined(__GNUC__) + 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; + } + +#endif /* __GNUC__ */ + return contents; +} + + +k_machreg_t +kdb_getcr(int regnum) +{ + k_machreg_t contents = 0; +#if defined(__GNUC__) + 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; + } + +#endif /* __GNUC__ */ + return contents; +} + +void +kdb_putdr6(k_machreg_t contents) +{ + kdb_putdr(6, contents); +} + +void +kdb_putdr7(k_machreg_t contents) +{ + kdb_putdr(7, contents); +} + +void +kdb_putdr(int regnum, k_machreg_t contents) +{ +#if defined(__GNUC__) + 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; + } +#endif +} + +/* + * Symbol table functions. + */ + +/* + * kdbgetsym + * + * Return the symbol table entry for the given symbol + * + * Parameters: + * symname Character string containing symbol name + * Outputs: + * Returns: + * NULL Symbol doesn't exist + * ksp Pointer to symbol table entry + * Locking: + * None. + * Remarks: + */ + +__ksymtab_t * +kdbgetsym(const char *symname) +{ + __ksymtab_t *ksp = __kdbsymtab; + int i; + + if (symname == NULL) + return NULL; + + for (i=0; i<__kdbsymtabsize; i++, ksp++) { + if (ksp->name && (strcmp(ksp->name, symname)==0)) { + return ksp; + } + } + + return NULL; +} + +/* + * kdbgetsymval + * + * Return the address of the given symbol. + * + * Parameters: + * symname Character string containing symbol name + * Outputs: + * Returns: + * 0 Symbol name is NULL + * addr Address corresponding to symname + * Locking: + * None. + * Remarks: + */ + +unsigned long +kdbgetsymval(const char *symname) +{ + __ksymtab_t *ksp = kdbgetsym(symname); + + return (ksp?ksp->value:0); +} + +/* + * kdbaddmodsym + * + * Add a symbol to the kernel debugger symbol table. Called when + * a new module is loaded into the kernel. + * + * Parameters: + * symname Character string containing symbol name + * value Value of symbol + * Outputs: + * Returns: + * 0 Successfully added to table. + * 1 Duplicate symbol + * 2 Symbol table full + * Locking: + * None. + * Remarks: + */ + +int +kdbaddmodsym(char *symname, unsigned long value) +{ + + /* + * Check for duplicate symbols. + */ + if (kdbgetsym(symname)) { + printk("kdb: Attempt to register duplicate symbol '%s' @ 0x%lx\n", + symname, value); + return 1; + } + + if (__kdbsymtabsize < __kdbmaxsymtabsize) { + __ksymtab_t *ksp = &__kdbsymtab[__kdbsymtabsize++]; + + ksp->name = symname; + ksp->value = value; + return 0; + } + + /* + * No room left in kernel symbol table. + */ + { + static int __kdbwarn = 0; + + if (__kdbwarn == 0) { + __kdbwarn++; + printk("kdb: Exceeded symbol table size. Increase KDBMAXSYMTABSIZE in scripts/genkdbsym.awk\n"); + } + } + + return 2; +} + +/* + * kdbdelmodsym + * + * Add a symbol to the kernel debugger symbol table. Called when + * a new module is loaded into the kernel. + * + * Parameters: + * symname Character string containing symbol name + * value Value of symbol + * Outputs: + * Returns: + * 0 Successfully added to table. + * 1 Symbol not found + * Locking: + * None. + * Remarks: + */ + +int +kdbdelmodsym(const char *symname) +{ + __ksymtab_t *ksp, *endksp; + + if (symname == NULL) + return 1; + + /* + * Search for the symbol. If found, move + * all successive symbols down one position + * in the symbol table to avoid leaving holes. + */ + endksp = &__kdbsymtab[__kdbsymtabsize]; + for (ksp = __kdbsymtab; ksp < endksp; ksp++) { + if (ksp->name && (strcmp(ksp->name, symname) == 0)) { + endksp--; + for ( ; ksp < endksp; ksp++) { + *ksp = *(ksp + 1); + } + __kdbsymtabsize--; + return 0; + } + } + + return 1; +} + +/* + * kdbnearsym + * + * Return the name of the symbol with the nearest address + * less than 'addr'. + * + * Parameters: + * addr Address to check for symbol near + * Outputs: + * Returns: + * NULL No symbol with address less than 'addr' + * symbol Returns the actual name of the symbol. + * Locking: + * None. + * Remarks: + */ + +char * +kdbnearsym(unsigned long addr) +{ + __ksymtab_t *ksp = __kdbsymtab; + __ksymtab_t *kpp = NULL; + int i; + + for(i=0; i<__kdbsymtabsize; i++, ksp++) { + if (!ksp->name) + continue; + + if (addr == ksp->value) { + kpp = ksp; + break; + } + if (addr > ksp->value) { + if ((kpp == NULL) + || (ksp->value > kpp->value)) { + kpp = ksp; + } + } + } + + /* + * If more than 128k away, don't bother. + */ + if ((kpp == NULL) + || ((addr - kpp->value) > 0x20000)) { + return NULL; + } + + return kpp->name; +} + +/* + * kdbgetregcontents + * + * 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 + * % - 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: + * + * Note that this function is really machine independent. The kdb + * register list is not, however. + */ + +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) }, + { " ss", offsetof(struct pt_regs, xss) }, + { " cs", offsetof(struct pt_regs, xcs) }, + { "eflags", offsetof(struct pt_regs, eflags) }, + + { " ds", offsetof(struct pt_regs, xds) }, + { " es", offsetof(struct pt_regs, xes) }, + { "origeax", offsetof(struct pt_regs, orig_eax) }, + +}; + +static const int nkdbreglist = sizeof(kdbreglist) / sizeof(struct kdbregs); + +int +kdbgetregcontents(const char *regname, + struct pt_regs *regs, + unsigned long *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); + return 0; + } + + if (regname[0] == '%') { + /* User registers: %%e[a-c]x, etc */ + regname++; + regs = (struct pt_regs *) + (current->tss.esp0 - sizeof(struct pt_regs)); + } + + 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 +kdbsetregcontents(const char *regname, + struct pt_regs *regs, + unsigned long contents) +{ + int i; + + if (regname[0] == '%') { + regname++; + regs = (struct pt_regs *) + (current->tss.esp0 - sizeof(struct pt_regs)); + } + + for (i=0; itss.esp0 - sizeof(struct pt_regs)); + } + + if (type == NULL) { + for (i=0; i + + /* + * This file provides definitions for functions that + * are dependent upon the product into which kdb is + * linked. + * + * This version is for linux. + */ +typedef void (*handler_t)(struct pt_regs *); +typedef unsigned long k_machreg_t; + +extern char* kbd_getstr(char *, size_t, char *); +extern int kdbinstalltrap(int, handler_t, handler_t*); +extern int kdbinstalldbreg(kdb_bp_t*); +extern void kdbremovedbreg(int); +extern void kdb_initbptab(void); +extern int kdbgetregcontents(const char *, struct pt_regs *, unsigned long *); +extern int kdbsetregcontents(const char *, struct pt_regs *, unsigned long); +extern int kdbdumpregs(struct pt_regs *, const char *, const char *); + + +/* + * kdb_db_trap is a processor dependent routine invoked + * from kdb() via the #db trap handler. It handles breakpoints involving + * the processor debug registers and handles single step traps + * using the processor trace flag. + */ + +#define KDB_DB_BPT 0 /* Straight breakpoint */ +#define KDB_DB_SS 1 /* Single Step trap */ +#define KDB_DB_SSB 2 /* Single Step, caller should continue */ + +extern int kdb_db_trap(struct pt_regs *); + + + /* + * Support for ia32 architecture debug registers. + */ +#define KDB_DBREGS 4 +extern k_machreg_t dbregs[]; + +#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 k_machreg_t kdb_getdr6(void); +extern void kdb_putdr6(k_machreg_t); + +extern k_machreg_t kdb_getdr7(void); +extern void kdb_putdr7(k_machreg_t); + +extern k_machreg_t kdb_getdr(int); +extern void kdb_putdr(int, k_machreg_t); + +extern k_machreg_t kdb_getcr(int); + +extern void kdb_bp_install(void); +extern void kdb_bp_remove(void); + +/* + * 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_jmp_buf; + +extern int kdb_setjmp(kdb_jmp_buf *); +extern void kdb_longjmp(kdb_jmp_buf *, int); + +extern kdb_jmp_buf kdbjmpbuf; + +#endif /* KDSUPPORT */ diff -urN ikd-ref/arch/i386/kdb/modules/Makefile ikd/arch/i386/kdb/modules/Makefile --- ikd-ref/arch/i386/kdb/modules/Makefile Thu Jan 1 01:00:00 1970 +++ ikd/arch/i386/kdb/modules/Makefile Tue Oct 3 20:08:45 2000 @@ -0,0 +1,19 @@ +# +# 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) + +#O_TARGET := kdbm_vm.o + +M_OBJS := kdbm_vm.o + +override CFLAGS := $(CFLAGS:%-pg=%-g -c) + +include $(TOPDIR)/Rules.make diff -urN ikd-ref/arch/i386/kdb/modules/kdbm_vm.c ikd/arch/i386/kdb/modules/kdbm_vm.c --- ikd-ref/arch/i386/kdb/modules/kdbm_vm.c Thu Jan 1 01:00:00 1970 +++ ikd/arch/i386/kdb/modules/kdbm_vm.c Tue Oct 3 20:08:45 2000 @@ -0,0 +1,82 @@ +#define MODULE +#include +#include +#include + +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, "" } +}; + +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; + + addr += offset; + + for (i=0; imask; tp++) { + if (vp.vm_flags & tp->mask) { + printk("%s ", tp->name); + } + } + printk("\n"); + + return 0; +} + +int +init_module(void) +{ + kdb_register("vm", kdbm_vm, "", "Display vm_area_struct", 0); + + return 0; +} + +void +cleanup_module(void) +{ + kdb_unregister("vm"); +} diff -urN ikd-ref/arch/i386/kdb/pc_keyb.h ikd/arch/i386/kdb/pc_keyb.h --- ikd-ref/arch/i386/kdb/pc_keyb.h Thu Jan 1 01:00:00 1970 +++ ikd/arch/i386/kdb/pc_keyb.h Tue Oct 3 20:08:45 2000 @@ -0,0 +1,126 @@ +/* + * linux/drivers/char/pc_keyb.h + * + * PC Keyboard And Keyboard Controller + * + * (c) 1997 Martin Mares + */ + +/* + * Configuration Switches + */ + +#undef KBD_REPORT_ERR /* Report keyboard errors */ +#define KBD_REPORT_UNKN /* Report unknown scan codes */ +#define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */ +#undef KBD_IS_FOCUS_9000 /* We have the brain-damaged FOCUS-9000 keyboard */ +#undef INITIALIZE_MOUSE /* Define if your PS/2 mouse needs initialization. */ + + + +#define KBD_INIT_TIMEOUT 1000 /* Timeout in ms for initializing the keyboard */ +#define KBC_TIMEOUT 250 /* Timeout in ms for sending to keyboard controller */ +#define KBD_TIMEOUT 1000 /* Timeout in ms for keyboard command acknowledge */ + +/* + * Internal variables of the driver + */ + +extern unsigned char pckbd_read_mask; +extern unsigned char aux_device_present; + +/* + * Keyboard Controller Registers + */ + +#define KBD_STATUS_REG 0x64 /* Status register (R) */ +#define KBD_CNTL_REG 0x64 /* Controller command register (W) */ +#define KBD_DATA_REG 0x60 /* Keyboard data register (R/W) */ + +/* + * Keyboard Controller Commands + */ + +#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */ +#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */ +#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */ +#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */ +#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */ +#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */ +#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */ +#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */ +#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */ +#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */ +#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if + initiated by the auxiliary device */ +#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */ + +/* + * Keyboard Commands + */ + +#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ +#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ +#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ +#define KBD_CMD_DISABLE 0xF5 /* Disable scanning */ +#define KBD_CMD_RESET 0xFF /* Reset */ + +/* + * Keyboard Replies + */ + +#define KBD_REPLY_POR 0xAA /* Power on reset */ +#define KBD_REPLY_ACK 0xFA /* Command ACK */ +#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ + +/* + * Status Register Bits + */ + +#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */ +#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */ +#define KBD_STAT_SELFTEST 0x04 /* Self test successful */ +#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */ +#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */ +#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */ +#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */ +#define KBD_STAT_PERR 0x80 /* Parity error */ + +#define AUX_STAT_OBF (KBD_STAT_OBF | KBD_STAT_MOUSE_OBF) + +/* + * Controller Mode Register Bits + */ + +#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */ +#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */ +#define KBD_MODE_SYS 0x04 /* The system flag (?) */ +#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */ +#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */ +#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */ +#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */ +#define KBD_MODE_RFU 0x80 + +/* + * Mouse Commands + */ + +#define AUX_SET_RES 0xE8 /* Set resolution */ +#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ +#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ +#define AUX_GET_SCALE 0xE9 /* Get scaling factor */ +#define AUX_SET_STREAM 0xEA /* Set stream mode */ +#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */ +#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */ +#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */ +#define AUX_RESET 0xFF /* Reset aux device */ + +#define AUX_BUF_SIZE 2048 + +struct aux_queue { + unsigned long head; + unsigned long tail; + struct wait_queue *proc_list; + struct fasync_struct *fasync; + unsigned char buf[AUX_BUF_SIZE]; +}; diff -urN ikd-ref/arch/i386/kernel/Makefile ikd/arch/i386/kernel/Makefile --- ikd-ref/arch/i386/kernel/Makefile Tue Oct 3 06:14:17 2000 +++ ikd/arch/i386/kernel/Makefile Tue Oct 3 20:08:45 2000 @@ -75,7 +75,28 @@ O_OBJS += visws_apic.o endif +ifdef CONFIG_KMSGDUMP +O_OBJS += kmsgdump.o +endif + head.o: head.S $(TOPDIR)/include/linux/tasks.h $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $*.S -o $*.o + +kmsgdump.o: kmsgdump.S $(TOPDIR)/include/asm/kmsgdump.h + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $*.S -o $*.o + +# 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 + +# avoid the nmi irq to happen inside a spin_lock_irq in the mcount suff +ifeq ($(CONFIG_DEBUG_MCOUNT),y) +ifeq ($(CONFIG_NMI_WATCHDOG),y) +traps.o: traps.c + $(CC) $(CFLAGS:%-pg=%-g -c) $(EXTRA_CFLAGS) -c -o $@ $< +endif +endif include $(TOPDIR)/Rules.make diff -urN ikd-ref/arch/i386/kernel/entry.S ikd/arch/i386/kernel/entry.S --- ikd-ref/arch/i386/kernel/entry.S Tue Oct 3 20:08:37 2000 +++ ikd/arch/i386/kernel/entry.S Tue Oct 3 20:08:45 2000 @@ -42,6 +42,7 @@ #include #include +#include #include #define ASSEMBLY #include @@ -161,6 +162,19 @@ GET_CURRENT(%ebx) 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_INT + call 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 @@ -172,6 +186,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 $0x20,flags(%ebx) # PF_TRACESYS @@ -182,6 +203,11 @@ .globl ret_from_sys_call .globl ret_from_intr ret_from_sys_call: +#ifdef CONFIG_DEBUG_MCOUNT + pushl %eax + call SYMBOL_NAME(mcount) + popl %eax +#endif movl SYMBOL_NAME(bh_mask),%eax andl SYMBOL_NAME(bh_active),%eax jne handle_bottom_half @@ -199,16 +225,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 @@ -304,9 +349,21 @@ jmp error_code ENTRY(nmi) +#ifdef CONFIG_NMI_WATCHDOG + pushl %eax + SAVE_ALL + movl %esp,%edx +#endif pushl $0 +#ifndef CONFIG_NMI_WATCHDOG pushl $ SYMBOL_NAME(do_nmi) jmp error_code +#else + pushl %edx + call SYMBOL_NAME(do_nmi) + addl $8,%esp + RESTORE_ALL +#endif /* CONFIG_NMI_WATCHDOG */ ENTRY(int3) pushl $0 diff -urN ikd-ref/arch/i386/kernel/i386_ksyms.c ikd/arch/i386/kernel/i386_ksyms.c --- ikd-ref/arch/i386/kernel/i386_ksyms.c Tue Oct 3 06:14:17 2000 +++ ikd/arch/i386/kernel/i386_ksyms.c Tue Oct 3 20:08:45 2000 @@ -17,6 +17,9 @@ #include #include #include +#if defined(CONFIG_KDB) +#include +#endif extern void dump_thread(struct pt_regs *, struct user *); extern int dump_fpu(elf_fpregset_t *); @@ -119,6 +122,20 @@ #ifdef CONFIG_VT EXPORT_SYMBOL(screen_info); +#endif + +#if defined(CONFIG_KDB) +EXPORT_SYMBOL(kdb_register); +EXPORT_SYMBOL(kdb_unregister); +EXPORT_SYMBOL(kdbgetword); +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); #endif EXPORT_SYMBOL(rtc_lock); diff -urN ikd-ref/arch/i386/kernel/io_apic.c ikd/arch/i386/kernel/io_apic.c --- ikd-ref/arch/i386/kernel/io_apic.c Tue Sep 5 02:28:38 2000 +++ ikd/arch/i386/kernel/io_apic.c Tue Oct 3 20:08:45 2000 @@ -21,6 +21,11 @@ */ #define IO_APIC_BASE(idx) ((volatile int *)__fix_to_virt(FIX_IO_APIC_BASE_0 + idx)) +#ifdef CONFIG_NMI_WATCHDOG +int nmi_pin = -1; +const int nmi_irq = CONFIG_NMI_WATCHDOG_IRQ; +#endif + /* * The structure of the IO-APIC: */ @@ -638,6 +643,18 @@ if (!apic && !IO_APIC_IRQ(irq)) continue; +#ifdef CONFIG_NMI_WATCHDOG + if (irq == nmi_irq) { + entry.delivery_mode = 4; /* broadcast NMI */ + make_8259A_irq(irq); + /* + * Remember which register has the NMI IRQ entry, + * so we can turn it off in case there is some + * screwup + */ + nmi_pin = pin; + } +#endif entry.vector = assign_irq_vector(irq); @@ -1196,6 +1213,10 @@ * 0x80, because int 0x80 is hm, kind of importantish. ;) */ for (i = 0; i < NR_IRQS ; i++) { +#ifdef CONFIG_NMI_WATCHDOG + if (i == nmi_irq) + continue; +#endif if (IO_APIC_VECTOR(i) > 0) { if (IO_APIC_irq_trigger(i)) irq_desc[i].handler = &ioapic_level_irq_type; @@ -1237,6 +1258,12 @@ { int pin1, pin2; +#ifdef CONFIG_NMI_WATCHDOG + if (nmi_pin == -1) + printk(".. NMI watchdog has invalid source IRQ.\n"); + else if (nmi_irq != -1) + printk("NMI Watchdog activated on source IRQ %d\n", nmi_irq); +#endif pin1 = find_timer_pin(mp_INT); pin2 = find_timer_pin(mp_ExtINT); enable_IO_APIC_irq(0); @@ -1274,6 +1301,10 @@ } } printk(" works.\n"); +#ifdef CONFIG_NMI_WATCHDOG + if ((nmi_pin != -1) && (nmi_irq == 0)) + printk("NMI Watchdog disabled (source IRQ was 0)!\n"); +#endif } } diff -urN ikd-ref/arch/i386/kernel/irq.c ikd/arch/i386/kernel/irq.c --- ikd-ref/arch/i386/kernel/irq.c Tue Jun 13 03:48:12 2000 +++ ikd/arch/i386/kernel/irq.c Tue Oct 3 20:08:45 2000 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -323,6 +324,9 @@ BUILD_SMP_INTERRUPT(invalidate_interrupt) BUILD_SMP_INTERRUPT(stop_cpu_interrupt) BUILD_SMP_INTERRUPT(call_function_interrupt) +#if defined(CONFIG_KDB) +BUILD_SMP_INTERRUPT(kdb_interrupt) +#endif /* CONFIG_KDB */ BUILD_SMP_INTERRUPT(spurious_interrupt) /* @@ -824,6 +828,8 @@ kstat.irqs[cpu][irq]++; irq_desc[irq].handler->handle(irq, ®s); + MCOUNT(); + /* * This should be conditional: we should really get * a return code from the irq handler to tell us @@ -834,6 +840,8 @@ if (bh_active & bh_mask) do_bottom_half(); } + + MCOUNT(); } int setup_x86_irq(unsigned int irq, struct irqaction * new) @@ -1099,7 +1107,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]); } @@ -1131,6 +1143,11 @@ /* IPI vector for APIC spurious interrupts */ set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); + +#if defined(__SMP__) && defined(CONFIG_KDB) + /* IPI vector for Kernel Debugger */ + set_intr_gate(KDB_VECTOR, kdb_interrupt); +#endif /* CONFIG_SMP */ #endif request_region(0x20,0x20,"pic1"); request_region(0xa0,0x20,"pic2"); diff -urN ikd-ref/arch/i386/kernel/irq.h ikd/arch/i386/kernel/irq.h --- ikd-ref/arch/i386/kernel/irq.h Tue Oct 3 19:20:09 2000 +++ ikd/arch/i386/kernel/irq.h Tue Oct 3 20:16:39 2000 @@ -50,6 +50,9 @@ #define FIRST_EXTERNAL_VECTOR 0x20 #define SYSCALL_VECTOR 0x80 +#if defined(CONFIG_KDB) +#define KDBENTER_VECTOR 0x81 +#endif /* * Vectors 0x20-0x2f are used for ISA interrupts. @@ -66,6 +69,9 @@ #define INVALIDATE_TLB_VECTOR 0x31 #define STOP_CPU_VECTOR 0x40 #define LOCAL_TIMER_VECTOR 0x41 +#if defined(CONFIG_KDB) +#define KDB_VECTOR 0x42 +#endif /* CONFIG_KDB */ #define CALL_FUNCTION_VECTOR 0x50 /* diff -urN ikd-ref/arch/i386/kernel/kmsgdump.S ikd/arch/i386/kernel/kmsgdump.S --- ikd-ref/arch/i386/kernel/kmsgdump.S Thu Jan 1 01:00:00 1970 +++ ikd/arch/i386/kernel/kmsgdump.S Tue Oct 3 20:08:45 2000 @@ -0,0 +1,895 @@ +/* + * file : kmsgdump.S + * version : 0.4.2/2000042301 + * author : Willy Tarreau + * + * This code is called from the Bios when the processor has been + * externally reset and the NVRAM has been set to tell the bios + * to quickly branch to the pointer at [0x40:0x67]. + * It executes in real mode, and starts from 0:0x700. + * + * Its purpose is to dump the last kernel messages onto a floppy disk + * or to print them on a parallel printer. + * + * Useful information are stored at the following hexadecimal adresses : + * 0:0700 - 0FAF = this code followed by the stack + * 0:0FB0 - 0FEF = bios stack frame at reboot time + * 0:0FF0 ( 8 bits ) = destination drive number (bios number) + * 0:0FF1 ( 8 bits ) = track number (<256 !) + * 0:0FF2, bit 7 = caller mode (0=panic, 1=SysRq) + * bit 6 = output format (0=RAW, 1=FAT) + * bits 5->3 = behaviour when called from panic() : + * 000 = no dump, just halt + * 001 = no dump, just reboot + * 010 = dump, then halt + * 011 = dump, then reboot + * 100 = go to manual operation, just like SysRq. + * others undefined, so don't use please. Thanks. + * 0:0FF3 (13 bytes) = not used yet + * + * 0:1000 - 4FFF = kernel messages buffer contents (16 kB). + * + * Warning: This code is 16-bits, while the rest of the kernel is 32-bits. + * I couldn't find a way to link this file together with 32-bits + * files, just because of relative references which caused + * relocation issues. As a last resort, every variable is referenced + * "variable-base+CODEORIGIN" which is absolute and links well :-/ + * If someone finds a better way to do this, I'd like to learn from + * him. + */ +#include +#include + +#define _pause jmp .+2 +.text +.code16 + +.global kmsgdump +.global kmsgdump_end + +/* + * I use this only for development, it's a bios bootstrap. + * You only have to define UNDER_DEVEL and the binary object + * can be copied to a raw diskette and will boot from it. + * Moreover, the code contains the KMSGDUMP identifier so that + * the diskette is useable as-is. + */ +#ifdef UNDER_DEVEL +bootstrap: + jmp 0f + .byte 0x90 + .ascii "KMSGDUMP" +0: + cli + xorw %ax, %ax + movw %ax, %ss + movw $0x7C00 , %sp + pushw %ss + popw %es + movw %cs, %ax + movw %ax, %ds + movw $1000,%cx + movw $(kmsgdump-bootstrap+0x7c00), %si + movw $CODEORIGIN, %di + cld + rep + movsb +/* movw $MASK_SYSRQMODE, MODEFLAGS*/ + movw $(MASK_PANICDUMP|MASK_PANICBOOT|MASK_OUTPUTFAT|MASK_SAFEDUMP), MODEFLAGS + +# read remaining sectors + + movw $0x0203, %ax + movw $(bootstrap-kmsgdump+CODEORIGIN+0x200), %bx + movw $0x0002, %cx + movw $0x0000, %dx + int $0x13 + +# pre-read messages + mov $0x220, %ax /* ah=2 (read), al=32 (32 sectors) */ + mov $(BEGINOFMESSAGES), %bx + mov $0x0005, %cx + mov $0, %dx /* head always begins with 0 */ + int $0x13 + + ljmp $0, $CODEORIGIN +#endif /* UNDER_DEVEL */ + +kmsgdump: + cli + xorw %ax, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw $(TOPOFSTACK), %sp + call hardware_reset + sti + testb $(MASK_SYSRQMODE | MASK_PANICMAN), MODEFLAGS + jz automatic + +/* Here is the manual mode loop (either SysRq or Panic with MANUAL flag set). + * Each second, a short beep is sent so that the user knows the computer + * is waiting for him/her to press a key. + * When 'B' is hit, the system immediately reboots. When 'H' is hit, the + * system halts, and when another key is hit, the system attempts a dump + * and beeps two more times in case of success. It then goes on waiting + * for a key in case the user wants another dump. + */ +manual_dump: +/* reinitialize display to color_80x25 */ + movw $VIDEOMODE, %ax + int $0x10 +/* hide the cursor */ + movb $1, %ah + movw $0xFFFF,%cx + int $0x10 +/* redraw the screen */ + xorw %cx, %cx + call clearwindow +/* display title */ + movw $MKWORD(0,22), %dx + movw $(title-kmsgdump+CODEORIGIN), %si + call displayline +/* display separation and help line */ + movw $MKWORD(HELPLINE, 0), %dx + call movecursor + movw $MKWORD(0x0A, 177), %ax /* write a line of hashes */ + movb $0, %bh + movw $(RIGHTCOL+1), %cx /* a full line */ + int $0x10 + movw $MKWORD(HELPLINE, 12), %dx /* display help at column 12 */ + movw $(helpline-kmsgdump+CODEORIGIN), %si + call displayline + +# now we'll have to print the messages buffer in the lower window. +redrawmessages: + movw $MKWORD(HELPLINE+2,0), %cx /* first line of the messages window */ + pushw %cx /* save this, it will serve us later */ + call clearwindow + popw %dx /* restore top of window */ + mov $(BEGINOFMESSAGES), %si /* SI points to the beginning of the messages */ + xorw %di,%di /* first line */ + jmp findline +1: lodsb + or %al,%al /* end of data ? */ + jnz 3f + movw %di, currentline-kmsgdump+CODEORIGIN /* we fix the end at this point. */ + jmp waitevent /* and don't show anything */ +3: cmp $10, %al + jnz 1b /* while not LF, look for an end of line */ + inc %di /* count one more line */ +findline: + cmpw currentline-kmsgdump+CODEORIGIN, %di + jb 1b +showmessages: + cmpw $(LOG_BUF_LEN+BEGINOFMESSAGES), %si /* end of buffer reached */ + jnb waitevent + call displayline + cmp $BOTTOMLINE, %dh /* stop displaying when screen is full */ + ja waitevent + or %al,%al /* or when end of messages is reached */ + jnz showmessages +# redisplay current parameters after each pressed key +waitevent: + cmpb $0, mustredraw-kmsgdump+CODEORIGIN /* if we must redraw the messages window, let's do it */ + movb $0, mustredraw-kmsgdump+CODEORIGIN + jnz redrawmessages + +/* now, we'll display current settings (drive, format, status ...) */ + movw $MKWORD(2, 8), %dx /* row 2, col 8 */ + movw $(msgprinter-kmsgdump+CODEORIGIN), %si + call displayline + + movb DRIVENUM,%al + addb $'A', %al /* A and B if < 0x80 */ + test $0x80, %al + jz 1f + subb $(0x80-'C'+'A'), %al /* C and D if >= 0x80 */ +1: movb %al, unitname-kmsgdump+CODEORIGIN /* write the drive letter into the string */ + movw $MKWORD(2, 40), %dx /* row 2, col 40*/ + movw $(msgunit-kmsgdump+CODEORIGIN), %si + call displayline +/* SI is already positionned at msgFAT, so we avoid "movw $msgFAT,%si" */ + testb $(MASK_OUTPUTFAT), MODEFLAGS + jnz 2f + movw $(msgRAW-kmsgdump+CODEORIGIN), %si +2: call displayline + movw $MKWORD(3, 8), %dx /* row 3, col 8 */ + movw $(kversion-kmsgdump+CODEORIGIN), %si + call displayline + +1: movb $STATUS_WAITING, %al + call showstatus +8: call waitkey + jnz 1f /* a key has been hit */ + call beep /* no key hit: let's beep once a second */ + jmp waitevent /* loop while no key is pressed */ +1: cmpb $0x50, %ah /* down arrow */ + jnz 5f +7: incw currentline-kmsgdump+CODEORIGIN /* go one line below */ +1: movb $1, mustredraw-kmsgdump+CODEORIGIN + mov $1, %ah /* we check if another key has been hit to speed up scrolling */ + int $0x16 + jnz 8b /* handle this new key */ + jmp waitevent +5: cmpb $0x48, %ah /* up arrow */ + jnz 6f + subw $1,currentline-kmsgdump+CODEORIGIN /* one line back */ + jc 7b /* aie, negative line. Let's correct this */ + jmp 1b +6: cmpb $'b', %al /* 'B' stands for 'boot' */ + jz do_reboot + cmpb $'h', %al /* 'H' stands for 'halt' */ + jz do_halt + cmpb $'f', %al /* 'F' stands for 'change Format' */ + jnz 3f + xorw $(MASK_OUTPUTFAT), MODEFLAGS + jmp waitevent +3: cmpb $'u', %al /* 'U' stands for 'change Unit' */ + jnz 4f + xorb $1, DRIVENUM /* altern first/second drive */ + jmp waitevent +4: cmpb $'t', %al /* 'T' stands for 'prinTer' */ + jnz 5f + call probeprinter + jmp waitevent +5: cmpb $'i', %al /* 'T' stands for 'prinTer' */ + jnz 6f + movw $MKWORD(HELPLINE+2,0), %cx /* first line of the messages window */ + pushw %cx /* save this, it will serve us later */ + call clearwindow + popw %dx /* restore top of window */ + movw $(aboutmsg-kmsgdump+CODEORIGIN), %si +5: call displayline + or %al, %al + jnz 5b + jmp waitevent +6: cmpb $'p', %al /* 'P' stands for 'Print' */ + jnz 7f + call printbuffer + jmp 8f /* display same return codes as for diskette */ +7: cmpb $'d', %al /* 'D' stands for 'dump' */ + jnz waitevent + call do_dump +8: movb $STATUS_WERR, %al /* assume a dump error for now */ + jc 9f /* on error, we return immediately after ony one beep */ + movb $STATUS_DUMPOK, %al /* else we say that's good */ + call beep /* make 3 audible beeps once successfully dumped */ + call beep +9: call showstatus + call beep + movw $16,%cx /* wait 1 second after the message is displayed */ +0: call sleep54 + loop 0b + jmp waitevent + +/* clears the screen from the upper left corner defined by CX to the bottom right of the screen */ +clearwindow: + movw $MKWORD(BOTTOMLINE,RIGHTCOL), %dx /* bottom of screen */ + movw $0x0600, %ax /* clear the messages window */ + movb $ATTRIB, %bh + int $0x10 + ret + +/* probes the next printer, and if not found, the next one, ... until + we find one which is OK, or we fall back to the current one. +*/ +probeprinter: + movb $STATUS_PROBING, %al + call showstatus + movw $3, %cx /* we allow to automatically probe 3 printers */ +0: movb currentlpt-kmsgdump+CODEORIGIN, %al + incw %ax + cmpb $'3', %al + jbe 1f + mov $'1', %al +1: movb %al, currentlpt-kmsgdump+CODEORIGIN + subb $'1', %al + movzbw %al, %dx /* printer number */ + movb $1, %ah /* initialize printer */ + int $0x17 + testb $0x2F, %ah /* status = OK ? */ + loopnz 0b /* no, probe next printer */ +2: ret + +/* prints all the buffer on the current printer. On error, CF is set. */ +printbuffer: + mov $(BEGINOFMESSAGES), %si /* SI points to the beginning of the messages */ + movb $STATUS_PRINTING, %al + call showstatus + movb currentlpt-kmsgdump+CODEORIGIN, %al + subb $'1', %al + movzbw %al, %dx /* printer number */ +0: cmpw $(BEGINOFMESSAGES+LOG_BUF_LEN), %si + jae 9f /* end of buffer */ + lodsb + or %al, %al /* end of data */ + jz 9f + cmp $10, %al /* line feed : we'll insert a carriage return */ + jnz 1f + mov $MKWORD(0, 13), %ax /* print carriage return */ + int $0x17 + mov $10, %al /* line feed */ +1: movb $0, %ah /* print character */ + int $0x17 + testb $0x2F, %ah /* status = OK ? */ + jz 0b /* fetch next char */ + stc + ret +9: movw $MKWORD(0, 12), %ax /* form feed*/ + int $0x17 + clc + ret + +/* Automatic dump mode, without user operation. The function was called + * from panic(). Mode flags also allow an immediate reboot without a dump, + * which is the same than playing with "panic_timeout" in case of a kernel + * panic. + */ +automatic: + testb $(MASK_PANICDUMP), MODEFLAGS + jz 1f + testb $(MASK_SAFEDUMP), MODEFLAGS + jz 0f + /* a safe dump is requested. we must ensure the diskette contains a "KMSGDUMP" + label before scratching it. */ + xorw %ax,%ax /* reinitialize drive */ + movzbw DRIVENUM, %dx + int $0x13 + + movw $0x201, %ax /* read 1 sector */ + movw $(FREEMEMORY), %bx /* pointer to the buffer */ + movw $0x1, %cx /* from sector 1 */ + call int13retry + jc 1f /* read error, don't dump */ + cmpl $0x47534d4b, FREEMEMORY+3 /* "KMSG" */ + jnz 1f + cmpl $0x504d5544, FREEMEMORY+7 /* "DUMP" */ + jnz 1f +0: call do_dump +1: testb $(MASK_PANICBOOT), MODEFLAGS + jnz do_reboot + jmp do_halt + +/* beep(): + * Make a 54 ms beep, and silently pause during 54 ms + */ +beep: call sound_on + call sleep54 + call sound_off + call sleep54 + ret + +sleep54: + pushw %ax + movw CLOCKTICKS, %ax + incw %ax +0: cmpw CLOCKTICKS, %ax + jnb 0b + popw %ax + ret + +/* waitkey(): + * Wait at most 3 second for a key press. + * If no key is pressed during that time, 0 is returned in AX and ZF is set. + * If a key is pressed, its lower case ascii code is returned in AL, its scan + * code in AH and the ZF is cleared. + */ +waitkey: + pushw %cx + movw CLOCKTICKS, %cx + addw $55, %cx /* 55/18.2 = 3 seconds */ +0: movb $1, %ah /* function="tell if a key was pressed" */ + int $0x16 + jnz 1f + cmpw CLOCKTICKS, %cx + jnb 0b + xorw %ax,%ax /* timeout, no key */ + jmp 2f +1: movb $0, %ah + int $0x16 + cmpb $'A',%al + jb 2f + cmpb $'Z',%al + ja 2f + orb $0x20,%al +2: popw %cx + orw %ax,%ax /* ZF=(AX==0) */ + ret + + +/* hardware_reset(): + Reconfigure what can be wrong now in hardware : + - interrupt controller + - timers + - keyboard + - disk units +*/ +hardware_reset: +/* PIC reconfiguration consists in mapping PIC#1 IRQ 0 to software INT 8. + * Although not needed, PIC#2 is initialized in case we meet buggy chipsets. + */ + + movb $0x11, %al /* reconfiguration */ + out %al, $0x20 +# out %al, $0xA0 + _pause + movb $8, %al /* map irq 0 to int 0x08 */ + out %al, $0x21 +# movb $0x70, %al /* map irq 8 to int 0x70 */ +# out %al, $0xA1 + _pause + movb $4, %al /* master mode for PIC#1 */ + out %al, $0x21 +# movb $2, %al /* slave mode for PIC#2 */ +# out %al, $0xa1 + _pause + movb $1, %al /* 8086 mode */ + out %al, $0x21 +# out %al, $0xA1 + _pause + movb $0xFF, %al /* all IRQs are masked at the moment */ + out %al, $0x21 +# out %al, $0xA1 + _pause + +/* Disable internal speaker output if it was enabled */ + + call sound_off + +/* PIT reconfiguration : set Timer0 clock to 18.2 Hz */ + + movb $0x34, %al /* timer 0 in interrupt mode */ + out %al, $0x43 + _pause + movb $0, %al /* divide by 65536 (1193182 Hz -> 18.2 Hz) */ + out %al, $0x40 /* LSB first */ + _pause + out %al, $0x40 /* MSB next */ + _pause + +/* PIT reconfiguration (cont'd) : set Timer2 clock to 880 Hz for the buzzer */ + + movb $0xb6, %al /* timer 2 in square wave mode */ + out %al, $0x43 + _pause + movw $1355, %ax /* divider set to 1193182/1355 = 880 Hz */ + out %al, $0x42 /* LSB first */ + _pause + movb %ah, %al + out %al, $0x42 /* MSB next */ + _pause + + movb $0xBC, %al /* only irq 0,1,6 unmasked */ + out %al, $0x21 + _pause + +/* Keyboard reset: The keyboard may be misconfigured. We will send it a + reconfiguration (max speed) so that the Bios will at least reconfigure it + and its controller (i8042). +*/ + + mov $0x305, %ax /* set speed */ + xorw %bx, %bx /* full speed */ + int $0x16 + +/* Reset the minimum so that int 13 works */ + movw $0x3F2, %dx + movb $0x0, %al + outb %al, %dx + _pause + _pause + movb $0xC, %al + outb %al, %dx + _pause + _pause + + xorw %ax, %ax + xorw %dx, %dx + int $0x13 + ret + +/* sound_off(): disable internal speaker output. */ +sound_off: + push %ax + in $0x61, %al + andb $0xFC, %al + jmp 1f /* same code below */ + +/* sound_on(): enable internal speaker output. */ +sound_on: + push %ax + in $0x61, %al + orb $3, %al +1: _pause + out %al, $0x61 + pop %ax + ret + +/* HALT the CPU */ +do_halt: + movb $STATUS_HALTING, %al + call showstatus +0: cli + hlt + jmp 0b /* just in case of NMIs */ + + +/* REBOOT the CPU */ +do_reboot: + movb $STATUS_BOOTING, %al + call showstatus + cli +/* Make sure [40:72] doesn't contain 0x1234, which the Bios will interprete as + Ctrl-Alt-Del. */ + movw $0, 0x472 + ljmp $0xFFFF,$0 /* branch to bios boot strap */ + +/* + * do_dump(): + * This function dumps the message buffer onto a disk. CF is set if an error + * occured. + * + */ +do_dump: + movb DRIVENUM, %dl + xorw %ax, %ax + int $0x13 /* reinitialize drive */ + xorw %si, %si /* assume first data sector = 0 */ + testb $(MASK_OUTPUTFAT), MODEFLAGS + jz 1f + movw $(FATSIZE*2+2), %si /* when FAT is used, data go further */ +1: call makefat + jc 2f + call dumpdata +2: ret + +/* + * Shows, in the status line, the message matching the status code in AL. + * AX, BX, DX and FLAGS contents are lost. CX and SI are kept. + */ +showstatus: + cbw + pushw %si + pushw %cx + testb $(MASK_SYSRQMODE | MASK_PANICMAN), MODEFLAGS + jz 2f /* is not in interactive mode, don't show anything */ + pushw %ax + movw $MKWORD(3, 40), %dx + movw $(msgstatus-kmsgdump+CODEORIGIN), %si + call displayline + popw %si + cmpw $STATUS_MAXMSG, %si + ja 2f + shlw $4, %si + addw $(statwait-kmsgdump+CODEORIGIN), %si +1: call displayline +2: popw %cx + popw %si + ret +/* + * sets the cursor to the begin of the line in DH, col in DL, and displays the message from SI + * which ends with \n, \r or \0. On return, AL contains the byte that ended the line, and SI + * points to the next byte. + * if LF is encountered, the cursor is positionned at the beginning of the next line and + * LF is returned in AL, the cursor position in DX. + * If DH is set to -1, then the cursor isn't moved. The new cursor position is returned into + * DX so it's possible to recall displayline immediately. + */ +displayline: + cmpb $-1,%dh + jz 0f + call movecursor +0: cld +1: movb $ATTRIB, %bl /* use standard text attribute by default */ + lodsb + cmp $0377, %al /* a 0377 char means next one will be highlit */ + jnz 3f + movb $HIGHLIGHT, %bl + lodsb +3: cmp $13, %al /* CR is ignored */ + jz 1b + or %al, %al /* end of messages */ + jz 2f + cmp $10, %al /* LF: position onto next line and return from function */ + jnz 5f + pushw %ax + call findcursor /* DX gets the current cursor position */ + movb $255, %dl /* if we pass the end of the line, we'll begin on a new row */ + call movecursor + popw %ax /* restore LF in AX */ + jmp 2f /* and we return */ +5: movb $0, %bh /* current page = 0 */ + movw $1, %cx /* 1 char */ + movb $9, %ah /* write char with attribute */ + int $0x10 + call findcursor /* DX gets the current cursor position */ + inc %dx /* next position */ + call movecursor + jmp 1b +movecursor: + cmp $RIGHTCOL, %dl /* did we override the end of the line ? */ + jbe 1f /* no */ + inc %dh /* yes, so position onto the next line */ + movb $0, %dl +1: movb $2, %ah +4: movb $0, %bh + int $0x10 +2: ret +findcursor: + mov $3, %ah + jmp 4b + +#if defined(UNDER_DEVEL) & defined(DEBUG) +showhex: + pushfw + pushaw + movw %ax, %si + movw $4, %cx +0: pushw %cx + rolw $4, %si + movw %si, %ax + andb $0xF, %al + addb $'0', %al + cmpb $'9', %al + jbe 1f + addb $7, %al +1: movb $0xE, %ah + int $0x10 + popw %cx + loop 0b +#ifdef STEP_BY_STEP + movb $0, %ah + int $0x16 +#endif + popaw + popfw + ret +#endif + + +/* + * makefat(): + * writes a 18-sectors/track FAT on the diskette, at logical sector 0. + * CF is set if an error occurs. + * SI is preserved. + * + */ +makefat: + cld + pushw %si + movb $STATUS_FORMATING, %al + call showstatus +/* make DI point to the first free memory */ + movw $(FREEMEMORY), %di + pushw %di /* will be used later */ +/* initialize the data area : 1 boot, 1 fat, 1 root */ + movw $((FATSIZE+2)<<8), %cx + cld + xorw %ax, %ax + rep + stosw + popw %di + pushw %di +/* make boot sector */ + movw $(bootsect-kmsgdump+CODEORIGIN), %si + movw $(rootsect-bootsect), %cx + rep + movsb + +/* make fat1 (which is also fat2) */ + movw $(FREEMEMORY+0x200), %di + movw $2, %bx /* first usable cluster is always 2 */ + movw $(LOG_BUF_LEN >> 9), %cx /* #of sectors per FAT */ + + addw %bx, %cx /* decide now of the "end of chain" */ + incw %bx + movb $0xF0, %al /* FAT ID */ + stosb + movw $-1, %ax /* beginning of FAT */ + stosw +0: movw %bx, %ax + stosb + incw %bx /* next cluster */ + cmpw %cx, %bx /* did we reach the last cluster ? */ + jb 1f + movw $0xFFF, %bx /* yes: mark "end of chain" */ +1: shlb $4, %ah + movb $0, %al + orw %bx, %ax + rolw $4, %ax + stosw /* write 2 bytes at once */ + incw %bx /* next cluster */ + testb $0x10, %bh /* are we above the "end of chain" magic number ? */ + jz 0b /* not yet, let's go on for 2 more clusters */ + +/* make root sector */ + movw $(FREEMEMORY+0x200+FATSIZE<<9), %di + movw $(datasect-rootsect), %cx + rep + movsb + movw $(FIRSTCLUSTER), 14(%di) /* file begins at cluster 2 */ + movw $(LOG_BUF_LEN), 16(%di) /* file length in bytes */ + +/* in memory, we now have : + * BOOT | FAT | ROOT. + * We'll use a trick to write the two FATs : + * we first write (BOOT+FAT), and then append (FAT+ROOT) + */ + popw %di /* beginning of the area to write */ + movw $(1+FATSIZE), %cx /* one boot sector + N FAT sectors */ + xorw %si, %si /* write onto the first sector */ + pushw %cx /* fortunately, it will be the same size after... */ + call wrsect /* let's write the boot sector onto the disk */ + popw %cx /* 1+FATSIZE, let's save one byte of code */ + jc 0f +/* now write fat2 + root */ + movw $(FREEMEMORY+0x200), %di + call wrsect /* let's write the boot sector onto the disk */ +0: popw %si + ret + +/* + * dumpdata(): + * writes bytes of data on drive DRIVENUM, from logical sector %si. + * CF is set if an error occured. + * + */ +dumpdata: + clc + pushw %si + movb $STATUS_DUMPING, %al + call showstatus + movw $(LOG_BUF_LEN >> 9), %cx /* #of sectors that will be written */ + movw $(BEGINOFMESSAGES), %di + call wrsect + popw %si + ret + +/* this function calls int 13 and if any error occurs, it will retry twice. + * all registers are kept except flags. + */ +int13retry: + pushaw + clc + int $0x13 + jnc 0f + popaw + pushaw + clc + int $0x13 +0: popaw + ret + +/* + * wrsect(): + * starts writing CX sectors of data from ES:DI to the diskette at logical sector SI. + * first logical sector is numbered 0. This function can write more than one track at + * a time, and is limited to the buffer's address in ES segment. + * all registers are modified, particularly : + * - SI which contains the first sector that should be written next time + * - DI which points to next data to be written + * - CX which tells how many sectors should be still written in case of CF=1 + */ +wrsect: +0: jcxz 9f /* when there are no more sectors to write, we return */ + pushw %cx /* save this number of remaining sectors to write */ + movw %si, %ax + + movw $(NBHEADS*NBSECT), %cx /* we'll compute the starting track number */ + divb %cl + xchgb %al, %ch /* al = 0, ch = track number */ + xchgb %ah, %al /* ah = 0, al = head*nbsect + sect */ + movb $(NBSECT), %cl + divb %cl + movb %al, %dh /* dh = head number */ + xchgb %cl, %ah /* ah = NBSECT, cl = sector number (0-based) */ + subb %cl, %ah /* ah = number of sectors before end of track */ + popw %bx /* number of sectors to write */ + movzbw %ah, %ax /* max #of sectors left on this track */ + cmpw %ax, %bx /* does it fit ? */ + ja 1f /* no, let's use AX */ + movw %bx, %ax /* take all the remainder */ +1: + subw %ax, %bx /* subtract these sectors from the rest */ + addw %ax, %si /* increment the start sector for the next round */ + pushw %bx /* save the actual number of sectors to be written */ + movb $3, %ah /* 3=write, AL is already set to #sectors */ + incb %cl /* CL was 0-based and needs to be 1-based */ + movw %di, %bx /* pointer to beginning of data */ + movb DRIVENUM, %dl + /* at this point, AL=#sectors, AH=3(write), BX=buffer addr, */ + /* CL=starting sector, CH=track, DL=drivenum, DH=head num */ + /* let's compute the next DI now */ + pushw %ax + shlw $9, %ax /* AL*512 = data size */ + addw %ax, %di + popw %ax + call int13retry + popw %cx /* restore the number of remaining sectors */ + jnc 0b /* if no error, let's continue */ +9: ret + +/* data needed to make a tiny FAT file system. */ +bootsect: + /* Warning : DOS expects first byte to be either EB or E9 (jump) ! */ + .byte 0xEB,(bootcode-bootsect-2),0x90 + .ascii "KMSGDUMP" + .hword 512 /* 512 bytes/sector */ + .byte 1 /* 1 sector/cluster */ + .hword 1 /* 1 reserved sector */ + .byte 2 /* 2 FATs */ + .hword 16 /* 16 root directory entries (1 root sector) */ + + .hword NBCLUSTERS /* total clusters */ + .byte 0xF0 /* media descriptor */ + .hword FATSIZE /* # of sectors per FAT */ + .hword NBSECT /* # of sectors/track */ + .hword NBHEADS /* # of heads */ + /* now some code to redirect the boot to the first hard disk. This way, + after an automatic dump, even if the diskette is left in the drive and + the bios doesn't automatically go to the hard disk, the diskette will + try to do it itself. + */ +bootcode: + /* first we'll copy the boot code elsewhere. */ + cli + cld + xorw %ax,%ax + movw %ax,%es + movw %ax,%ds + movw $0x7c00,%si /* current source */ + movw $0x8000,%di /* new destination */ + movw $256,%cx /* 256 words = this entire sector */ + pushw %si + repz + movsw + popw %bx /* current code will be erased */ + jmp .+3+0x8000-0x7c00 /* jump to fresh copy of this code */ + incw %cx /* cx=1 : read from sector 1 (cx was 0, so now it's 1).*/ + movw $0x80, %dx /* 0x80 = first hard drive */ + movw $0x201,%ax /* 2=read, 1=1 sector */ + int $0x13 + jnc bootsect-(0x8000-0x7c00) /* jump to new code if no error */ + int $0x19 /* or try to reboot */ + jmp . /* hang if int 19 returns. */ + +rootsect: .ascii "MESSAGESTXT" + .byte 0x20 /* archived file attribute */ +datasect: /* end of FAT-specific data */ + +mustredraw: .byte 0 +currentline: .hword 0 +title: .asciz "Linux Kernel Messages Dumper - v0.4.2" +helpline: .asciz "> \377Boot \377Dump \377Format \377Halt \377Info \377Print prin\377Ter \377Unit \377U\377p \377D\377o\377w\377n <" +/* it's important to respect the same order for the following parts. */ +msgprinter: .ascii "Printer : \377L\377P\377T\377" +currentlpt: .asciz "1" +msgunit: .ascii "Drive Unit and Format : \377" /* no zero string -> will display drive letter */ +unitname: .asciz "A/" +msgFAT: .asciz "\377F\377A\377T\3771\3772" +msgRAW: .asciz "\377R\377A\377W " +kversion: .ascii "Kernel version : " + .asciz UTS_RELEASE +msgstatus: .asciz "Status : " + +/* these messages must be *exactly* 15 chars long, and terminated by a 0 (ie: 16 bytes tot) */ +statwait: .asciz "Press a key " +statform: .asciz "Formating FAT12" +statdump: .asciz "Dumping msgs " +statdmpd: .asciz "Messages dumped" +statboot: .asciz "Rebooting... " +stathalt: .asciz "System halted. " +statprob: .asciz "Probing printer" +statprnt: .asciz "Printing msgs " +statwerr: .asciz "Dump failed ! " +aboutmsg: .ascii "KMSGDUMP 0.4.2 - (C) 2000/04/23 - Willy Tarreau \n" + .ascii "<\377B> reboots, <\377D> dumps messages onto the floppy, <\377F> changes floppy\n" + .ascii "format, <\377H> halts the system, <\377P> prints the messages on the selected\n" + .ascii "printer, <\377T> selects a printer, <\377U> changes the floppy unit, \377U\377p/\377D\377o\377w\377n arrows\n" + .ascii "scroll messages up/down, and <\377I> shows this help. When an action completes\n" + .ascii "successfully (print, dump), you should hear 3 short beeps.\n" + .ascii "When dumping data onto a floppy, be warned that FLOPPY CONTENTS WILL BE \377L\377O\377S\377T.\n" + .asciz "Hit <\377U\377p> key to list kernel messages again." +/* just a public label at the end of the code so we know its lenght. */ +kmsgdump_end: diff -urN ikd-ref/arch/i386/kernel/process.c ikd/arch/i386/kernel/process.c --- ikd-ref/arch/i386/kernel/process.c Thu May 4 13:00:36 2000 +++ ikd/arch/i386/kernel/process.c Tue Oct 3 20:08:45 2000 @@ -43,6 +43,10 @@ #include #endif +#if defined(CONFIG_KMSGDUMP) +#include +#endif + #include "irq.h" spinlock_t semaphore_wake_lock = SPIN_LOCK_UNLOCKED; @@ -378,6 +382,263 @@ } +#ifdef CONFIG_KMSGDUMP +extern void dump_syslog(char * buf); + +/* checkpoint counter used to try to recover from recursive system crashes */ +static int chkpnt = 0; + +/* boot-time default flags. These are single character indicators except for T (track number) + and U (unit number) which must be followed by a number. To avoid confusion with default + values, each flag has its complement and you are strongly encouraged to use one of each. + + flags available are : + Name Description Default Complement + F FAT mode Yes R + R Raw mode F + A Automatic mode Yes I + I Interactive mode A + B Boot after dump Yes H only used in automatic mode + H Halt after dump B only used in automatic mode + S Safe mode Yes O only used in automatic mode + O Overwrite disk S only used in automatic mode + E Enable dumping Yes D only used in automatic mode + D Disable dumping E only used in automatic mode + Txxx Track xxx 0 (N/A) first track is 0 per default + Uxxx Unit xxx 0 (N/A) bios drive is 0 (A:) per default +*/ +char kmsgdump_flags[16]= +#ifdef CONFIG_KMSGDUMP_FAT + "F" /* Fat */ +#else + "R" /* Raw */ +#endif +#ifdef CONFIG_KMSGDUMP_AUTO + "AEB" /* Auto, Dump, Boot */ +#else + "IE" /* Interactive, Dump */ +#endif +#ifdef CONFIG_KMSGDUMP_SAFE + "S" +#else + "O" +#endif + "T0U0"; /* Safe,Track0,Unit0 */ + +/* + * machine_dump() : Added on 19990628 by Willy Tarreau + * + * This is used to dump the kernel messages buffer onto a floppy diskette. + * The system is rebooted to real mode so it will be unuseable after a call + * to this function which never returns. + * The argument to this function is the caller type (DUMP_FROM_PANIC=0 or + * DUMP_FROM_SYSRQ=1). + * + * Note 1 : Part of this code has been shamelessly stolen from machine_restart(). + * Perhaps it should be better to implement a generic rebooting function. + * Note 2 : I couldn't manage to get the method to work + * here. The bios never calls my code. After hundreds of reboots, + * I finally accepted the fact that the PC is stronger than the human + * and only kept the reset method. But be careful, Linus' comment + * about it not working properly on some hardware still applies here. + * Note 3 : I don't know what means we have on other architectures to dump + * messages, but it will be interesting to add this feature to all archs. + */ + +void machine_dump(int callertype) +{ + int modeflags=MASK_OUTPUTFAT | MASK_PANICDUMP | MASK_PANICBOOT | MASK_SAFEDUMP; + int track=0; /* by default, track 0 */ + int unit=0; /* and drive A */ + + if (chkpnt<100) { + chkpnt=100; +#if __SMP__ + /* + * turn off the IO-APIC, so we can do a clean reboot + */ + init_pic_mode(); +#endif + chkpnt++; + cli(); + chkpnt++; + } + /* Write 0x09 to CMOS register number 0x0f, which the BIOS POST + routine will recognize as telling it to : + - re-initialize a few parts of hardware ; + - load registers from a structure pointed to at [40:67] ; + - branch to the program at offset 0x14 in this structure. + + [Note: funciton 0x0A is easier to use, but doesn't re-initialize + anything and might not work on specific harware. ] + */ + + if (chkpnt<200) { + chkpnt=200; + outb_p (0x8f, 0x70); + chkpnt++; + outb_p (0x09, 0x71); + chkpnt++; + } + + /* Remap the kernel at virtual address zero, as well as offset zero + from the kernel segment. This assumes the kernel segment starts at + virtual address PAGE_OFFSET. */ + + if (chkpnt<300) { + chkpnt=300; + memcpy (swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS, + sizeof (swapper_pg_dir [0]) * KERNEL_PGD_PTRS); + chkpnt++; + } + + /* Make sure the first page is mapped to the start of physical memory. + It is normally not mapped, to trap kernel NULL pointer dereferences. + */ + + if (chkpnt<400) { + int page; + + chkpnt=400; + for (page=0; page < ((FREEMEMORY+PAGE_SIZE-1) >> PAGE_SHIFT); page++) + pg0[page] = _PAGE_RW | _PAGE_PRESENT; + chkpnt++; + } + + /* + * Use `swapper_pg_dir' as our page directory. We bother with + * `SET_PAGE_DIR' because although might be rebooting, but if we change + * the way we set root page dir in the future, then we wont break a + * seldom used feature ;) + */ + + if (chkpnt<500) { + chkpnt=500; + SET_PAGE_DIR(current,swapper_pg_dir); + chkpnt++; + } + + /* to avoid that the bios thinks we pressed Ctrl-Alt-Del : */ + if (chkpnt<600) { + chkpnt=600; +#ifdef REALLY_USE_REBOOT_SETUP + reboot_setup("c", NULL); + chkpnt++; + *((unsigned short *)0x472) = reboot_mode; +#else + *((unsigned short *)0x472) = 0x0; +#endif + chkpnt++; + } + /* For the switch to real mode, copy some code to low memory, out of + the way of BIOS variables. */ + + if (chkpnt<700) { + chkpnt=700; + memset ((char *)CODEORIGIN, 0, 0x1000-CODEORIGIN); /* get a clean first page */ + chkpnt++; + + /* the main real mode code */ + memcpy ((char *)CODEORIGIN, kmsgdump, kmsgdump_end-kmsgdump); + chkpnt++; + } + + /* configure parameters from kmsgdump_flags */ + if (chkpnt<800) { + char *c; + int *var = NULL; + int val = 0; + chkpnt = 800; + + c = kmsgdump_flags; + while (*c) { + switch(*c) { + case 'F' : modeflags |= MASK_OUTPUTFAT; break; + case 'R' : modeflags &= ~MASK_OUTPUTFAT; break; + case 'I' : modeflags |= MASK_PANICMAN; break; + case 'A' : modeflags &= ~MASK_PANICMAN; break; + case 'B' : modeflags |= MASK_PANICBOOT; break; + case 'H' : modeflags &= ~MASK_PANICBOOT; break; + case 'S' : modeflags |= MASK_SAFEDUMP; break; + case 'O' : modeflags &= ~MASK_SAFEDUMP; break; + case 'E' : modeflags |= MASK_PANICDUMP; break; + case 'D' : modeflags &= ~MASK_PANICDUMP; break; + case 'T' : var = &track; val = 0; break; + case 'U' : var = &unit; val = 0; break; + } + if (*c>='0' && *c<='9') + val=(val*10)+(*c-'0'); + c++; + /* when the number ends, assign it to the variable */ + if ((*c<'0' || *c>'9') && (var != NULL)) { + *var = val; + } + } + chkpnt++; + + *(char *)DRIVENUM = unit; + chkpnt++; + *(char *)TRACKNUM = track; + chkpnt++; + + /* this will be configurable one day too :-) */ + *(char *)MODEFLAGS = modeflags; + chkpnt++; + } + + + if (chkpnt<900) { + chkpnt=900; + if (callertype == DUMP_FROM_SYSRQ) + *(char *)MODEFLAGS |= MASK_SYSRQMODE; + chkpnt++; + } + + + if (chkpnt<1000) { + chkpnt=1000; + memset (phys_to_virt(BEGINOFMESSAGES), 0, LOG_BUF_LEN); /* pre-initialize the messages buffer */ + chkpnt++; + dump_syslog(phys_to_virt(BEGINOFMESSAGES)); /* get last messages */ + chkpnt++; + } + + /* We only have to inform the Bios to where we need to jump, + and initialize an image of the register bank in memory. */ + if (chkpnt<1100) { + chkpnt=1100; + *((unsigned short *)(BOOTSTACKFRAME+0x16)) = 0; /* CS */ + chkpnt++; + *((unsigned short *)(BOOTSTACKFRAME+0x14)) = CODEORIGIN; /* IP */ + chkpnt++; + + *((unsigned short *)0x467) = ((int)BOOTSTACKFRAME) & 0xFFFF; + chkpnt++; + *((unsigned short *)0x469) = ((int)BOOTSTACKFRAME)>>4 & 0xF000; + chkpnt++; + } + + /* now we have to hardly reset the CPU. */ + if (chkpnt<1200) { + chkpnt=1200; + for (;;) { + int i; + for (i=0; i<100; i++) { + kb_wait(); + udelay(50); + outb(0xfe,0x64); /* pulse reset low */ + udelay(50); + } + /* That didn't work - force a triple fault.. */ + __asm__ __volatile__("lidt %0": :"m" (no_idt)); + __asm__ __volatile__("int3"); + } + chkpnt++; + } +} +#endif /* CONFIG_KMSGDUMP */ + + void show_regs(struct pt_regs * regs) { long cr0 = 0L, cr2 = 0L, cr3 = 0L; @@ -700,7 +961,7 @@ * More important, however, is the fact that this allows us much * more flexibility. */ -void __switch_to(struct task_struct *prev, struct task_struct *next) +void STDCALL(__switch_to(struct task_struct *prev, struct task_struct *next)) { /* Do the FPU save and set TS if it wasn't set before.. */ unlazy_fpu(prev); diff -urN ikd-ref/arch/i386/kernel/smp.c ikd/arch/i386/kernel/smp.c --- ikd-ref/arch/i386/kernel/smp.c Tue Oct 3 20:08:37 2000 +++ ikd/arch/i386/kernel/smp.c Tue Oct 3 20:08:45 2000 @@ -42,6 +42,10 @@ #include #include +#if defined(CONFIG_KDB) +#include +#endif + #include "irq.h" #define JIFFIE_TIMEOUT 100 @@ -104,6 +108,9 @@ unsigned long cpu_present_map = 0; /* Bitmask of physically existing CPUs */ unsigned long cpu_online_map = 0; /* Bitmask of currently online CPUs */ +#if defined(__SMP__) +unsigned long smp_kdb_wait = 0; /* Bitmask of waiters */ +#endif /* CONFIG_SMP */ int smp_num_cpus = 1; /* Total count of live CPUs */ int smp_threads_ready=0; /* Set when the idlers are all forked */ volatile int cpu_number_map[NR_CPUS]; /* which CPU maps to which logical number */ @@ -1972,6 +1979,21 @@ return 0; } + +#if defined(CONFIG_KDB) +void smp_kdb_stop(int all) +{ + if (all) { + smp_kdb_wait = 0xffffffff; + clear_bit(smp_processor_id(), &smp_kdb_wait); + send_IPI_allbutself(KDB_VECTOR); + } else { + set_bit(smp_processor_id(), &smp_kdb_wait); + send_IPI_self(KDB_VECTOR); + } +} +#endif + /* * Local timer interrupt handler. It does both profiling and * process statistics/rescheduling. @@ -1992,8 +2014,10 @@ * updated with atomic operations). This is especially * useful with a profiling multiplier != 1 */ +#ifndef CONFIG_PROFILE_GCC if (!user_mode(regs)) x86_do_profile(regs->eip); +#endif if (!--prof_counter[cpu]) { int user=0,system=0; @@ -2054,8 +2078,17 @@ * [ if a single-CPU system runs an SMP kernel then we call the local * interrupt as well. Thus we cannot inline the local irq ... ] */ +#ifdef CONFIG_NMI_WATCHDOG +atomic_t apic_timer_irqs [NR_CPUS] = { ATOMIC_INIT(0), }; +#endif void smp_apic_timer_interrupt(struct pt_regs * regs) { +#ifdef CONFIG_NMI_WATCHDOG + /* + * the only thing that can lock an NMI is an unACK-ed APIC ... + */ + atomic_inc(apic_timer_irqs+smp_processor_id()); +#endif /* * NOTE! We'd better ACK the irq immediately, * because timer handling can be slow, and we @@ -2107,6 +2140,51 @@ { stop_this_cpu(); } + +#if defined(CONFIG_KDB) +asmlinkage void smp_kdb_interrupt(int a) +{ + /* + * The stub function which calls this routine pushes + * the register context on the stack prior to calling + * this routine. No arguments are pushed on the stack, + * so taking the address of a non-existant first argument + * will obtain the address of the preserved register context + * + * This is a gross hack, but it works and doesn't require + * changes to BUILD_SMP_INTERRUPT. + */ + struct pt_regs *regs = &a; + extern unsigned long smp_kdb_wait; + extern int kdb_new_cpu; + ack_APIC_irq(); + + /* + * Wait for kdb to set us free. + */ +#if 0 + printk("stopping cpu %d for kdb\n", smp_processor_id()); +#endif + + while (test_bit(smp_processor_id(), &smp_kdb_wait)) + ; +#if 0 + printk("restarting cpu %d for kdb\n", smp_processor_id()); +#endif + + /* + * If we are the new CPU, call the kernel debugger here. + */ + if (kdb_new_cpu == smp_processor_id()) { + kdb(KDB_REASON_SWITCH, 0, regs); + } + + /* + * Re-establish any global breakpoint state + */ + kdb_global(smp_processor_id()); +} +#endif /* CONFIG_KDB */ asmlinkage void smp_call_function_interrupt(void) { diff -urN ikd-ref/arch/i386/kernel/time.c ikd/arch/i386/kernel/time.c --- ikd-ref/arch/i386/kernel/time.c Tue Oct 3 20:08:37 2000 +++ ikd/arch/i386/kernel/time.c Tue Oct 3 20:08:45 2000 @@ -392,8 +392,10 @@ * system, in that case we have to call the local interrupt handler. */ #ifndef __SMP__ +#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 ikd-ref/arch/i386/kernel/traps.c ikd/arch/i386/kernel/traps.c --- ikd-ref/arch/i386/kernel/traps.c Tue Oct 3 06:14:17 2000 +++ ikd/arch/i386/kernel/traps.c Tue Oct 3 20:08:45 2000 @@ -2,6 +2,8 @@ * linux/arch/i386/traps.c * * Copyright (C) 1991, 1992 Linus Torvalds + * + * 1998, Ingo Molnar, added NMI-Watchdog driver */ /* @@ -20,12 +22,18 @@ #include #include #include +#include /* NMI oopser watchdog */ +#include /* mcount debugger */ #ifdef CONFIG_MCA #include #include #endif +#if defined(CONFIG_KDB) +#include +#endif + #include #include #include @@ -45,6 +53,9 @@ #include "irq.h" asmlinkage int system_call(void); +#if defined(CONFIG_KDB) +asmlinkage int kdb_call(void); +#endif asmlinkage void lcall7(void); struct desc_struct default_ldt = { 0, 0 }; @@ -118,9 +129,55 @@ * segments. VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is * a guess of how much space is likely to be vmalloced. */ -#define VMALLOC_OFFSET (8*1024*1024) #define MODULE_RANGE (8*1024*1024) +#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 */ + static void show_registers(struct pt_regs *regs) { int i; @@ -156,12 +213,20 @@ printk("\nStack: "); stack = (unsigned long *) esp; for(i=0; i < kstack_depth_to_print; i++) { - if (((long) stack & 4095) == 0) + if (((long) stack & 8191) == 0) break; if (i && ((i % 8) == 0)) printk("\n "); printk("%08lx ", *stack++); } + +/* + * If tracing is switched on then we can walk the stack frame. Otherwise we + * can only guess. + */ +#ifdef CONFIG_KERNEL_DEBUGGING + print_call_trace_exact(regs, esp); +#else printk("\nCall Trace: "); if (!esp || (esp & 3)) printk("Bad ESP value."); @@ -171,7 +236,7 @@ module_start = PAGE_OFFSET + (max_mapnr << PAGE_SHIFT); module_start = ((module_start + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)); module_end = module_start + MODULE_RANGE; - while (((long) stack & 4095) != 0) { + while (((long) stack & 8191) != 0) { addr = *stack++; /* * If the address is either in the text segment of the @@ -191,6 +256,7 @@ } } } +#endif /* CONFIG_KERNEL_DEBUGGING */ printk("\nCode: "); if (!regs->eip || regs->eip==-1) printk("Bad EIP value."); @@ -208,6 +274,9 @@ { console_verbose(); spin_lock_irq(&die_lock); +#if defined(CONFIG_KDB) + kdb(KDB_REASON_PANIC, err, regs); +#endif printk("%s: %04lx\n", str, err & 0xffff); show_registers(regs); spin_unlock_irq(&die_lock); @@ -294,6 +363,7 @@ } } +#ifndef CONFIG_NMI_WATCHDOG static void mem_parity_error(unsigned char reason, struct pt_regs * regs) { printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n"); @@ -344,6 +414,73 @@ if (!(reason & 0xc0)) unknown_nmi_error(reason, regs); } +#else /* CONFIG_NMI_WATCHDOG */ + +extern atomic_t nmi_counter; + +/* + * FIXME: we assume here that the NMI came from the IO-APIC. It's a quite safe + * assumption in most cases, but if anyone knows a way to distinguish between + * NMI reasons, please speak up ... [i doubt that the IO-APIC does IO port 0x61 + * correctly] + */ + +extern atomic_t apic_timer_irqs [NR_CPUS]; +extern spinlock_t console_lock; +static spinlock_t nmi_print_lock = SPIN_LOCK_UNLOCKED; + +asmlinkage void do_nmi(struct pt_regs * regs, long error_code) +{ + /* + * the best way to detect wether 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. + * + * as these watchdog NMI IRQs are broadcasted to every CPU, here + * we only have to check the current processor. + * + * since NMIs dont listen to _any_ locks, we have to be extremely + * careful not to rely on unsafe variables. The printk might lock + * up though, so we have to break up console_lock first ... + * [when there will be more tty-related locks, break them up + * here too!] + */ + + static atomic_t last_irq_sums [NR_CPUS] = { ATOMIC_INIT(0), }; + static atomic_t alert_counter [NR_CPUS] = { ATOMIC_INIT(0), }; + + /* + * Since current-> is always on the stack, and we always switch + * the stack NMI-atomically, it's safe to use smp_processor_id(). + */ + int sum, cpu = smp_processor_id(); + + atomic_inc(&nmi_counter); + sum = atomic_read(apic_timer_irqs+cpu); + + if (atomic_read(last_irq_sums+cpu) == sum) { + /* + * Ayiee, looks like this CPU is stuck ... + * wait a few IRQs (5 seconds) before doing the oops ... + */ + atomic_inc(alert_counter+cpu); + if (atomic_read(alert_counter+cpu) == HZ/10) { +#ifdef CONFIG_DEBUG_MCOUNT + extern int sysctl_disable_mcount; + sysctl_disable_mcount = 1; +#endif + spin_lock(&nmi_print_lock); + printk("NMI Watchdog detected LOCKUP on CPU%d, registers:\n", cpu); + show_registers(regs); + spin_unlock(&nmi_print_lock); + do_exit(SIGSEGV); + } + } else { + atomic_set(last_irq_sums+cpu,sum); + atomic_set(alert_counter+cpu,0); + } +} +#endif /* CONFIG_NMI_WATCHDOG */ /* * Careful - we must not do a lock-kernel until we have checked that the @@ -410,9 +547,15 @@ return; clear_dr7: + +#if defined(CONFIG_KDB) + kdb(KDB_REASON_DEBUG, error_code, regs); +#else + __asm__("movl %0,%%db7" : /* no output */ : "r" (0)); +#endif /* CONFIG_KDB */ return; clear_TF: @@ -706,6 +849,13 @@ set_trap_gate(17,&alignment_check); set_trap_gate(18,&machine_check); set_system_gate(SYSCALL_VECTOR,&system_call); +#if defined(CONFIG_KDB) + /* + * A trap gate, used by the kernel to enter the + * debugger preserving all registers. + */ + set_trap_gate(KDBENTER_VECTOR,&kdb_call); +#endif /* set up GDT task & ldt entries */ set_tss_desc(0, &init_task.tss); diff -urN ikd-ref/arch/i386/mm/fault.c ikd/arch/i386/mm/fault.c --- ikd-ref/arch/i386/mm/fault.c Tue Oct 3 20:08:37 2000 +++ ikd/arch/i386/mm/fault.c Tue Oct 3 20:08:45 2000 @@ -4,6 +4,7 @@ * Copyright (C) 1995 Linus Torvalds */ +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include @@ -258,6 +260,8 @@ return; } + /* 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 ikd-ref/arch/i386/mm/init.c ikd/arch/i386/mm/init.c --- ikd-ref/arch/i386/mm/init.c Tue Oct 3 20:08:37 2000 +++ ikd/arch/i386/mm/init.c Tue Oct 3 20:08:45 2000 @@ -48,6 +48,7 @@ pmd_val(*pmd) = _PAGE_TABLE + __pa(BAD_PAGETABLE); } +#ifndef CONFIG_MEMLEAK /* put these back into pgtable.h */ pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) { pte_t *pte; @@ -91,6 +92,7 @@ } return (pte_t *) (pmd_page(*pmd) + offset); } +#endif CONFIG_MEMLEAK int do_check_pgt_cache(int low, int high) { diff -urN ikd-ref/arch/i386/vmlinux.lds.S ikd/arch/i386/vmlinux.lds.S --- ikd-ref/arch/i386/vmlinux.lds.S Tue Oct 3 06:14:17 2000 +++ ikd/arch/i386/vmlinux.lds.S Tue Oct 3 20:08:45 2000 @@ -28,6 +28,20 @@ _etext = .; /* End of text section */ + _skdb = .; + + .kdb : { + *(kdbsymtab) + *(kdbstrings) + } + _ekdb = .; + + + /* + * Allow 250k for kdb symbol table + */ + . = _skdb + (250 * 1024); + .data : { /* Data */ *(.data) CONSTRUCTORS diff -urN ikd-ref/arch/sparc64/kernel/smp.c ikd/arch/sparc64/kernel/smp.c --- ikd-ref/arch/sparc64/kernel/smp.c Thu May 4 13:00:36 2000 +++ ikd/arch/sparc64/kernel/smp.c Tue Oct 3 20:08:45 2000 @@ -598,6 +598,7 @@ static inline void sparc64_do_profile(unsigned long pc) { +#ifndef CONFIG_PROFILE_GCC if (prof_buffer && current->pid) { extern int _stext; @@ -608,6 +609,7 @@ pc = prof_len - 1; atomic_inc((atomic_t *)&prof_buffer[pc]); } +#endif } static unsigned long current_tick_offset; diff -urN ikd-ref/drivers/char/keyboard.c ikd/drivers/char/keyboard.c --- ikd-ref/drivers/char/keyboard.c Tue Oct 3 06:14:18 2000 +++ ikd/drivers/char/keyboard.c Tue Oct 3 20:08:45 2000 @@ -42,6 +42,9 @@ #include #include #include +#if defined(CONFIG_KDB) +#include +#endif #define SIZE(x) (sizeof(x)/sizeof((x)[0])) @@ -244,6 +247,12 @@ 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(KDB_REASON_KEYBOARD, 0, kbd_pt_regs); + } +#endif #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ if (keycode == SYSRQ_KEY) { diff -urN ikd-ref/drivers/char/serial.c ikd/drivers/char/serial.c --- ikd-ref/drivers/char/serial.c Tue Jun 13 03:48:13 2000 +++ ikd/drivers/char/serial.c Tue Oct 3 20:08:45 2000 @@ -375,8 +375,27 @@ mark_bh(SERIAL_BH); } +#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' flag to lilo and set the appropriate breakpoints. + */ + +extern int kdb_port; +static int kdb_serial_line = -1; +#endif /* CONFIG_KDB */ + static _INLINE_ void receive_chars(struct async_struct *info, +#if defined(CONFIG_KDB) + int *status, struct pt_regs * regs) +#else int *status) +#endif { struct tty_struct *tty = info->tty; unsigned char ch; @@ -386,6 +405,13 @@ 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(KDB_REASON_KEYBOARD, 0, regs); + break; + } +#endif if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; *tty->flip.char_buf_ptr = ch; @@ -612,7 +638,11 @@ printk("status = %x...", status); #endif if (status & UART_LSR_DR) +#if defined(CONFIG_KDB) + receive_chars(info, &status, regs); +#else receive_chars(info, &status); +#endif check_modem_status(info); if (status & UART_LSR_THRE) transmit_chars(info, 0); @@ -676,7 +706,11 @@ printk("status = %x...", status); #endif if (status & UART_LSR_DR) +#if defined(CONFIG_KDB) + receive_chars(info, &status, regs); +#else receive_chars(info, &status); +#endif check_modem_status(info); if (status & UART_LSR_THRE) transmit_chars(info, 0); @@ -739,7 +773,11 @@ printk("status = %x...", status); #endif if (status & UART_LSR_DR) +#if defined(CONFIG_KDB) + receive_chars(info, &status, regs); +#else receive_chars(info, &status); +#endif check_modem_status(info); if (status & UART_LSR_THRE) transmit_chars(info, 0); @@ -3543,6 +3581,18 @@ */ if (inb(ser->port + 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 = ser->port; + } +#endif /* CONFIG_KDB */ + return 0; } diff -urN ikd-ref/drivers/char/sysrq.c ikd/drivers/char/sysrq.c --- ikd-ref/drivers/char/sysrq.c Thu May 4 13:00:37 2000 +++ ikd/drivers/char/sysrq.c Tue Oct 3 20:08:45 2000 @@ -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 @@ -31,11 +33,20 @@ extern void wakeup_bdflush(int); extern void reset_vc(unsigned int); extern int console_loglevel; +extern void syslog_to_console(void); extern struct vfsmount *vfsmntlist; +#ifdef CONFIG_TRACE +#include +extern void ktrace_to_console(void); +#endif /* Machine specific power off function */ void (*sysrq_power_off)(void) = NULL; +#if defined(__i386__) && defined(CONFIG_KMSGDUMP) +extern void machine_dump(int); +#endif + /* Send a signal to all user processes */ static void send_sig_all(int sig, int even_init) @@ -85,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(); @@ -133,6 +144,33 @@ 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; +#if defined(__i386__) && defined(CONFIG_KMSGDUMP) + case 'd': /* D -- immediately dump the last kernel messages to a floppy. */ + printk("Trying to dump through real mode\n"); + machine_dump(1);/* 1 = "dump called from SysRq, so interactive mode expected" */ + break; +#endif default: /* Unknown: help */ if (kbd) printk("unRaw "); @@ -142,8 +180,11 @@ #endif printk("Boot "); if (sysrq_power_off) - printk("Off "); - printk("Sync Unmount showPc showTasks showMem loglevel0-8 tErm kIll killalL\n"); + printk("oFf "); +#if defined(__i386__) && defined(CONFIG_KMSGDUMP) + printk("Dump "); +#endif + printk("Sync Unmount showPc showTasks showMem loglevel0-8 tErm kIll killalL dumploGs Oops\n"); /* Don't use 'A' as it's handled specially on the Sparc */ } diff -urN ikd-ref/fs/proc/array.c ikd/fs/proc/array.c --- ikd-ref/fs/proc/array.c Tue Oct 3 20:08:37 2000 +++ ikd/fs/proc/array.c Tue Oct 3 20:08:45 2000 @@ -65,6 +65,7 @@ #include #include #include +#include #include #include @@ -227,6 +228,117 @@ &proc_profile_operations, }; +#ifdef CONFIG_TRACE + +/* + * 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; +} + +static struct file_operations proc_trace_operations = { + NULL, /* lseek */ + read_trace, + write_trace, +}; + +struct inode_operations proc_trace_inode_operations = { + &proc_trace_operations, +}; + +/* + * 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; +} +#endif /* CONFIG_TRACE */ static int get_loadavg(char * buffer) { diff -urN ikd-ref/fs/proc/root.c ikd/fs/proc/root.c --- ikd-ref/fs/proc/root.c Tue Oct 3 20:08:37 2000 +++ ikd/fs/proc/root.c Tue Oct 3 20:08:45 2000 @@ -21,6 +21,7 @@ #ifdef CONFIG_ZORRO #include #endif +#include /* * Offset of the first process in the /proc root directory.. @@ -504,6 +505,22 @@ NULL /* permission */ }; +#ifdef CONFIG_TRACE +static struct proc_dir_entry proc_root_trace = { + PROC_TRACE, 5, "trace", + S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, + 0, &proc_trace_inode_operations +}; +#endif + +#ifdef CONFIG_MEMLEAK +static struct proc_dir_entry proc_root_memleak = { + PROC_MEMLEAK, 7, "memleak", + S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, + 0, &proc_memleak_inode_operations +}; +#endif + static struct proc_dir_entry proc_root_loadavg = { PROC_LOADAVG, 7, "loadavg", S_IFREG | S_IRUGO, 1, 0, 0, @@ -737,6 +754,15 @@ proc_register(&proc_root, &proc_root_stram); #endif proc_register(&proc_root, &proc_root_slab); + +#ifdef CONFIG_MEMLEAK + proc_register(&proc_root, &proc_root_memleak); +#endif + +#ifdef CONFIG_TRACE + proc_register(&proc_root, &proc_root_trace); + proc_root_trace.size = sizeof(*trace_table); +#endif if (prof_shift) { proc_register(&proc_root, &proc_root_profile); diff -urN ikd-ref/include/asm-alpha/init.h ikd/include/asm-alpha/init.h --- ikd-ref/include/asm-alpha/init.h Mon Jan 17 16:44:43 2000 +++ ikd/include/asm-alpha/init.h Tue Oct 3 20:08:45 2000 @@ -1,6 +1,20 @@ #ifndef _ALPHA_INIT_H #define _ALPHA_INIT_H +#include + +#ifdef CONFIG_KERNEL_DEBUGGING +/* + * GCC bug, -pg doesnt mix well with section attribute ... :*( + */ +#define __init +#define __initdata +#define __initfunc(__arginit) __arginit +#define __INIT +#define __FINIT +#define __INITDATA + +#else /* !CONFIG_KERNEL_DEBUGGING */ #ifndef MODULE #define __init __attribute__ ((__section__ (".text.init"))) #define __initdata __attribute__ ((__section__ (".data.init"))) @@ -13,6 +27,8 @@ #define __FINIT .previous #define __INITDATA .section .data.init,"a" #endif + +#endif /* CONFIG_KERNEL_DEBUGGING */ #define __cacheline_aligned __attribute__((__aligned__(32))) diff -urN ikd-ref/include/asm-alpha/profiler.h ikd/include/asm-alpha/profiler.h --- ikd-ref/include/asm-alpha/profiler.h Thu Jan 1 01:00:00 1970 +++ ikd/include/asm-alpha/profiler.h Tue Oct 3 20:08:45 2000 @@ -0,0 +1,49 @@ +#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 + */ + +/* Dummy for now. Anybody care to supply code to get the stack size on + * an Alpha? KAO */ +#warning No real support for KSTACK on Alpha +#define get_stack_left() 4095 + +/* + * [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 ikd-ref/include/asm-i386/init.h ikd/include/asm-i386/init.h --- ikd-ref/include/asm-i386/init.h Mon Jan 17 16:44:44 2000 +++ ikd/include/asm-i386/init.h Tue Oct 3 20:13:27 2000 @@ -1,6 +1,25 @@ #ifndef _I386_INIT_H #define _I386_INIT_H +#include + +#ifdef CONFIG_KERNEL_DEBUGGING +/* + * GCC bug, -pg doesnt mix well with section attribute ... :*( + * memleak also needs the __init functions to stay resident + * due to ID structs (alloc_struct see mm.h) living in some init code. + */ +#define __init +#define __initdata +#define __initfunc(__arginit) __arginit +#define __INIT +#define __FINIT +#define __INITDATA + +#else +/* + * no -pg switch, we are cool and use init sections: + */ #define __init __attribute__ ((__section__ (".text.init"))) #define __initdata __attribute__ ((__section__ (".data.init"))) #define __initfunc(__arginit) \ @@ -10,6 +29,8 @@ #define __INIT .section ".text.init",#alloc,#execinstr #define __FINIT .previous #define __INITDATA .section ".data.init",#alloc,#write + +#endif #define __cacheline_aligned __attribute__ \ ((__section__ (".data.cacheline_aligned"))) diff -urN ikd-ref/include/asm-i386/keyboard.h ikd/include/asm-i386/keyboard.h --- ikd-ref/include/asm-i386/keyboard.h Tue Oct 3 19:16:51 2000 +++ ikd/include/asm-i386/keyboard.h Tue Oct 3 20:13:52 2000 @@ -38,6 +38,9 @@ #define kbd_sysrq_xlate pckbd_sysrq_xlate #define SYSRQ_KEY 0x54 +#if defined(CONFIG_KDB) +#define E1_PAUSE 119 /* PAUSE key */ +#endif /* resource allocation */ #define kbd_request_region() request_region(0x60, 16, "keyboard") diff -urN ikd-ref/include/asm-i386/kmsgdump.h ikd/include/asm-i386/kmsgdump.h --- ikd-ref/include/asm-i386/kmsgdump.h Thu Jan 1 01:00:00 1970 +++ ikd/include/asm-i386/kmsgdump.h Tue Oct 3 20:08:45 2000 @@ -0,0 +1,91 @@ +/* + * file : asm/kmsgdump.h + * version : 0.4.1/1999102301 + * author : Willy Tarreau + * + * Contents: several constants common to the ASM and C parts + * + */ + +/* LOG_BUF_LEN : should match 's */ +#ifndef LOG_BUF_LEN +#define LOG_BUF_LEN (16384) +#endif + +#define CODEORIGIN 0x0700 +#define TOPOFSTACK 0x0FB0 +#define BEGINOFMESSAGES 0x1000 +#define FREEMEMORY (BEGINOFMESSAGES+LOG_BUF_LEN) + +#define BOOTSTACKFRAME 0xFB0 +#define DRIVENUM 0xFF0 +#define TRACKNUM 0xFF1 +#define MODEFLAGS 0xFF2 + +/* bits definitions for MODEFLAGS */ +#define MASK_SYSRQMODE 0x80 +#define MASK_OUTPUTFAT 0x40 +#define MASK_PANICMAN 0x20 +#define MASK_PANICDUMP 0x10 +#define MASK_PANICBOOT 0x08 +#define MASK_SAFEDUMP 0x04 + +/* type of function which requested a dump */ +#define DUMP_FROM_PANIC 0 +#define DUMP_FROM_SYSRQ 1 + +#ifdef __ASSEMBLY__ + +#define CLOCKTICKS 0x46C +#define VIDEOMODE 3 +#define HELPLINE 5 +#define BOTTOMLINE 24 +#define RIGHTCOL 79 +#define ATTRIB 0x1B +#define HIGHLIGHT 0x1F + +/* specify here the format of the diskette, 1.44 MB by default */ +#define NBHEADS 2 +#define NBSECT 18 +#define NBTRACKS 80 + +/* let's compute some useful parameters from the diskette geometry */ +#define SECTPERCLUST 1 +#define FIRSTCLUSTER 2 + +/* At the moment, most versions of gas are broken so you can't activate + the following line which is the correct one. However, as long as the + constant SECTPERCLUST equals 1, the next line can be used as a replacement. + If you change the value of SECTPERCLUST, you'll have to change the + definition of NBCLUSTERS WITHOUT involving the divide operation which older + gas interpret as a comment. Versions from binutils 2.9.4 and later seem OK. +*/ + +#if THIS_GAS_IS_REALLY_GOOD +# define NBCLUSTERS ((NBHEADS*NBTRACKS*NBSECT)/SECTPERCLUST) +#else +# define NBCLUSTERS (NBHEADS*NBTRACKS*NBSECT) +#endif + +#define FATSIZE ((NBCLUSTERS*3+1023)>>10) + +#define STATUS_WAITING 0 +#define STATUS_FORMATING 1 +#define STATUS_DUMPING 2 +#define STATUS_DUMPOK 3 +#define STATUS_BOOTING 4 +#define STATUS_HALTING 5 +#define STATUS_PROBING 6 +#define STATUS_PRINTING 7 +#define STATUS_WERR 8 +#define STATUS_MAXMSG 8 + +#define MKWORD(h,l) ((h)<<8|(l)) + +#else /* C headers */ + +extern void kmsgdump(void); +extern void kmsgdump_end(void); + +#endif + diff -urN ikd-ref/include/asm-i386/pgtable.h ikd/include/asm-i386/pgtable.h --- ikd-ref/include/asm-i386/pgtable.h Tue Oct 3 20:15:11 2000 +++ ikd/include/asm-i386/pgtable.h Tue Oct 3 20:16:37 2000 @@ -206,7 +206,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 + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (FIXADDR_START) @@ -400,6 +404,7 @@ #define pte_quicklist (current_cpu_data.pte_quick) #define pgtable_cache_size (current_cpu_data.pgtable_cache_sz) +#ifndef CONFIG_MEMLEAK extern __inline__ pgd_t *get_pgd_slow(void) { pgd_t *ret = (pgd_t *)__get_free_page(GFP_KERNEL), *init; @@ -465,6 +470,120 @@ free_page((unsigned long)pte); } +#else /* CONFIG_MEMLEAK */ + +#define get_pgd_slow() \ +({ \ + pgd_t *_ret = (pgd_t *) __get_free_page(GFP_KERNEL), *init; \ + if (_ret) { \ + init = pgd_offset(&init_mm, 0); \ + memset (_ret, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); \ + memcpy (_ret + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD, \ + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); \ + } \ + _ret; \ +}) + +#define get_pgd_fast() \ +({ \ + unsigned long *_ret; \ + if((_ret = pgd_quicklist) != NULL) { \ + pgd_quicklist = (unsigned long *)(*_ret); \ + _ret[0] = _ret[1]; \ + 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)); \ +}) + +#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(_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(BAD_PAGETABLE); \ + _ret = NULL; \ + goto out_get_pte_slow; \ + } \ + free_page(_pte); \ + if (pmd_bad(*((pmd_t *)(pmd)))) { \ + __bad_pte((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(BAD_PAGETABLE); \ + _ret = NULL; \ + goto out_get_pte_kernel_slow; \ + } \ + free_page((unsigned long)_pte); \ + if (pmd_bad(*((pmd_t *)(pmd)))) { \ + __bad_pte_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 */ + /* We don't use pmd cache, so these are dummy routines */ extern __inline__ pmd_t *get_pmd_fast(void) { @@ -487,6 +606,7 @@ #define pgd_free(pgd) free_pgd_fast(pgd) #define pgd_alloc() get_pgd_fast() +#ifndef CONFIG_MEMLEAK extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address) { address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); @@ -527,6 +647,59 @@ __bad_pte(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_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)))) { \ + __bad_pte_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: \ + __bad_pte(((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 ikd-ref/include/asm-i386/profiler.h ikd/include/asm-i386/profiler.h --- ikd-ref/include/asm-i386/profiler.h Thu Jan 1 01:00:00 1970 +++ ikd/include/asm-i386/profiler.h Tue Oct 3 20:13:26 2000 @@ -0,0 +1,61 @@ +#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 & 0x1fff; \ +}) + +/* + * [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 (boot_cpu_data.x86_capability & 0x10) { \ + __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 ikd-ref/include/asm-i386/ptrace.h ikd/include/asm-i386/ptrace.h --- ikd-ref/include/asm-i386/ptrace.h Mon Jan 17 16:44:44 2000 +++ ikd/include/asm-i386/ptrace.h Tue Oct 3 20:08:45 2000 @@ -47,6 +47,29 @@ #define PTRACE_GETFPREGS 14 #define PTRACE_SETFPREGS 15 +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 ikd-ref/include/asm-i386/system.h ikd/include/asm-i386/system.h --- ikd-ref/include/asm-i386/system.h Tue Oct 3 19:16:25 2000 +++ ikd/include/asm-i386/system.h Tue Oct 3 20:13:26 2000 @@ -1,14 +1,20 @@ #ifndef __ASM_SYSTEM_H #define __ASM_SYSTEM_H +#include #include #include #ifdef __KERNEL__ struct task_struct; /* one of the stranger aspects of C forward declarations.. */ +#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 +#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" \ @@ -16,6 +22,8 @@ "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" \ @@ -28,6 +36,27 @@ "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" \ + "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 %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->tss.esp),"=m" (prev->tss.eip), \ + "=b" (last) \ + :"m" (next->tss.esp),"m" (next->tss.eip), \ + "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 ikd-ref/include/linux/kdb.h ikd/include/linux/kdb.h --- ikd-ref/include/linux/kdb.h Thu Jan 1 01:00:00 1970 +++ ikd/include/linux/kdb.h Tue Oct 3 20:08:45 2000 @@ -0,0 +1,177 @@ +/* + * Kernel Debugger + * + * Copyright 1999, Silicon Graphics, Inc. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + */ + +#if !defined(__KDB_H) +#define __KDB_H + +#include "bfd.h" + +#define KDB_MAJOR_VERSION 0 +#define KDB_MINOR_VERSION 5 + +/* + * Kernel Debugger Error codes + */ + +#define KDB_NOTFOUND -1 +#define KDB_GO -2 +#define KDB_ARGCOUNT -3 +#define KDB_BADWIDTH -4 +#define KDB_BADRADIX -5 +#define KDB_NOTENV -6 +#define KDB_NOENVVALUE -7 +#define KDB_NOTIMP -8 +#define KDB_ENVFULL -9 +#define KDB_ENVBUFFULL -10 +#define KDB_TOOMANYBPT -11 +#define KDB_TOOMANYDBREGS -12 +#define KDB_DUPBPT -13 +#define KDB_BPTNOTFOUND -14 +#define KDB_BADMODE -15 +#define KDB_BADINT -16 +#define KDB_INVADDRFMT -17 +#define KDB_BADREG -18 +#define KDB_CPUSWITCH -19 +#define KDB_BADCPUNUM -20 +#define KDB_BADLENGTH -21 +#define KDB_NOBP -22 + + /* + * XXX - machine dependent. + */ +#define KDB_ENTER() asm("\tint $129\n") + + /* + * kdb_active is initialized to zero, and is set + * to KDB_REASON_xxx whenever the kernel debugger is entered. + */ +extern int kdb_active; + + /* + * 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 + /* + * KDB_FLAG_SSB is set when the 'ssb' command is in progress. It + * indicates to the debug fault trap code that a trace fault + * (single step) should be continued rather than the debugger + * be entered. + */ +#define KDB_FLAG_SSB 0x00000002 + /* + * KDB_FLAG_SUPRESS is set when an error message is printed + * by a helper function such as kdbgetword. No further error messages + * will be printed until this flag is reset. Used to prevent + * an illegal address from causing voluminous error messages. + */ +#define KDB_FLAG_SUPRESS 0x00000004 + /* + * KDB_FLAG_FAULT is set when handling a debug register instruction + * fault. The backtrace code needs to know. + */ +#define KDB_FLAG_FAULT 0x00000008 + +extern int kdb_flags; + +extern int kdb_nextline; + + /* + * 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. + */ + +struct pt_regs; +extern int kdb(int reason, int error_code, struct pt_regs *); +#define KDB_REASON_ENTER 1 /* call debugger from source */ +#define KDB_REASON_FAULT 2 /* called from fault, eframe valid */ +#define KDB_REASON_BREAK 3 /* called from int 3, eframe valid */ +#define KDB_REASON_DEBUG 4 /* Called from int #DB, eframe valid */ +#define KDB_REASON_PANIC 5 /* Called from panic(), eframe valid */ +#define KDB_REASON_SWITCH 6 /* Called via CPU switch */ +#define KDB_REASON_INT 7 /* Called via int 129 */ +#define KDB_REASON_KEYBOARD 8 /* Called via keyboard interrupt */ + + /* + * The entire contents of this file from this point forward are + * private to the kernel debugger. They are subject to change + * without notice. + */ + + /* + * Breakpoint state + */ + +typedef struct _kdb_bp { + bfd_vma bp_addr; /* Address breakpoint is present at */ + unsigned char bp_prevbyte; /* Byte which the int3 replaced */ + + unsigned int bp_regtype:1; /* Uses a debug register (0-4) */ + unsigned int bp_enabled:1; /* Breakpoint is active in register */ + unsigned int bp_global:1; /* Global to all processors */ + unsigned int bp_free:1; /* This entry is available */ + unsigned int bp_data:1; /* Data breakpoint */ + unsigned int bp_write:1; /* Write data breakpoint */ + unsigned int bp_mode:2; /* 0=inst, 1=write, 2=io, 3=read */ + unsigned int bp_length:2; /* 0=1 byte, 1=2 bytes, 2=BAD, 3=4 */ + + int bp_reg; /* Register that this breakpoint uses */ + int bp_cpu; /* Cpu # (if bp_global == 0) */ +} kdb_bp_t; + +extern void kdb_global(int); /* Set global actions */ + + /* + * External Function Declarations + */ +extern char *kdbgetenv(const char *); +extern int kdbgetintenv(const char *, int *); +extern int kdbgetularg(const char *, unsigned long *); +extern int kdbgetaddrarg(int, const char**, int*, unsigned long *, long *, char **, struct pt_regs *); +extern unsigned long kdbgetword(unsigned long, int); +extern void kdb_id1(unsigned long); +extern void kdb_disinit(void); + + + /* + * External command function declarations + */ + +extern int kdb_id (int argc, const char **argv, const char **envp, struct pt_regs *); +extern int kdb_bp (int argc, const char **argv, const char **envp, struct pt_regs *); +extern int kdb_bc (int argc, const char **argv, const char **envp, struct pt_regs *); +extern int kdb_bt (int argc, const char **argv, const char **envp, struct pt_regs *); +extern int kdb_ss (int argc, const char **argv, const char **envp, struct pt_regs *); + + /* + * Symbol table format + */ + +typedef struct __symtab { + char *name; + unsigned long value; + } __ksymtab_t; + +extern __ksymtab_t __kdbsymtab[]; +extern int __kdbsymtabsize; +extern int __kdbmaxsymtabsize; + +extern unsigned long kdbgetsymval(const char *); +extern char * kdbnearsym(unsigned long); +extern int kdbaddmodsym(char *, unsigned long); +extern int kdbdelmodsym(const char *); + +typedef int (*kdb_func)(int, const char **, const char **, struct pt_regs*); + +extern int kdb_register(char *, kdb_func, char *, char *, short); +extern int kdb_unregister(char *); + +extern int kdb_printf(const char *,...); +#endif /* __KDB_H */ diff -urN ikd-ref/include/linux/kernel.h ikd/include/linux/kernel.h --- ikd-ref/include/linux/kernel.h Tue Oct 3 20:08:37 2000 +++ ikd/include/linux/kernel.h Tue Oct 3 20:13:26 2000 @@ -8,6 +8,7 @@ #ifdef __KERNEL__ #include +#include #include /* Optimization barrier */ @@ -19,7 +20,11 @@ #define LONG_MAX ((long)(~0UL>>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 KERN_EMERG "<0>" /* system is unusable */ #define KERN_ALERT "<1>" /* action must be taken immediately */ @@ -34,10 +39,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 extern void math_error(void); diff -urN ikd-ref/include/linux/memleak_unwrap.h ikd/include/linux/memleak_unwrap.h --- ikd-ref/include/linux/memleak_unwrap.h Thu Jan 1 01:00:00 1970 +++ ikd/include/linux/memleak_unwrap.h Tue Oct 3 20:14:17 2000 @@ -0,0 +1,93 @@ +#ifndef _MM_UNWRAP_H +#define _MM_UNWRAP_H + +#include + +#ifdef CONFIG_MEMLEAK + +#ifdef MEMLEAK_UNWRAP_PAGE +/* mm/page_alloc.c */ +#undef __get_free_pages +#define __get_free_pages(gfp_mask,gfporder) \ + __get_free_pages_wrap((gfp_mask),(gfporder),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_cache_slabmgmt(cachep,objp,local_flags) \ + kmem_cache_slabmgmt_wrap((cachep),(objp),(local_flags),IDPTR) +#endif /* MEMLEAK_UNWRAP_SLAB */ + +#ifdef MEMLEAK_UNWRAP_VMALLOC +/* mm/vmalloc.c */ +#undef vmalloc +#undef get_vm_area +#undef alloc_area_pte +#undef alloc_area_pmd +#undef vmalloc_area_pages + +#define get_vm_area(size) get_vm_area_wrap((size),IDPTR) +#define alloc_area_pte(pte, address, size) alloc_area_pte_wrap((pte),(address),(size),IDPTR) +#define alloc_area_pmd(pmd, address, size) alloc_area_pmd_wrap((pmd),(address),(size),IDPTR) +#define vmalloc_area_pages(address, size) vmalloc_area_pages_wrap((address),(size),IDPTR) +#endif /* MEMLEAK_UNWRAP_VMALLOC */ + +#ifdef MEMLEAK_UNWRAP_SKBUFF +/* net/core/skbuff.c */ +#undef alloc_skb +#undef skb_clone +#undef skb_copy +#undef skb_realloc_headroom + +#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_realloc_headroom(skb,newheadroom) skb_realloc_headroom_wrap((skb),(newheadroom),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 */ + +#endif /* CONFIG_MEMLEAK */ +#endif /* _MM_UNWRAP_H */ diff -urN ikd-ref/include/linux/mm.h ikd/include/linux/mm.h --- ikd-ref/include/linux/mm.h Tue Oct 3 20:08:37 2000 +++ ikd/include/linux/mm.h Tue Oct 3 20:15:22 2000 @@ -6,6 +6,7 @@ #ifdef __KERNEL__ +#include #include extern unsigned long max_mapnr; @@ -299,6 +300,7 @@ * goes to clearing the page. If you want a page without the clearing * overhead, just use __get_free_page() directly.. */ +#ifndef CONFIG_MEMLEAK #define __get_free_page(gfp_mask) __get_free_pages((gfp_mask),0) #define __get_dma_pages(gfp_mask, order) __get_free_pages((gfp_mask) | GFP_DMA,(order)) extern unsigned long FASTCALL(__get_free_pages(int gfp_mask, unsigned long gfp_order)); @@ -312,6 +314,101 @@ clear_page(page); return page; } + +#define MEMLEAK_ALLOC(addr) {} +#define MEMLEAK_FREE(addr) {} +#define MEMLEAK_ALLOC_NOLOCK(addr) {} +#define MEMLEAK_FREE_TRUE(expr,addr) {} + +#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 unsigned long memleak_init (unsigned long, unsigned long); +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 +#define PROBLEM() printk(KERN_ERR "MEMLEAK PROBLEM: <%s,%d>.\n",__FILE__,__LINE__) +#else +#define PROBLEM() {} +#endif /* MEMLEAK_PARANOID */ + +extern unsigned long FASTCALL(__get_free_pages_wrap(int gfp_mask, + unsigned long gfporder, struct alloc_struct *IDPTR)); + +#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) \ +({ \ + if(alloc_addr_lock((unsigned long)(addr),&MEMLEAKID)) \ + PROBLEM(); \ +}) +#else +#define MEMLEAK_ALLOC(addr) \ +({ \ + if(alloc_addr_lock((unsigned long)(addr),IDPTR)) \ + PROBLEM(); \ +}) +#define MEMLEAK_ALLOC_NOLOCK(addr) \ +({ \ + if(alloc_addr_nolock((unsigned long)(addr),IDPTR)) \ + PROBLEM(); \ +}) +#endif /* MEMLEAK_PASS_ALLOCATION */ + +#define MEMLEAK_FREE(addr) \ +({ \ + if(free_addr((unsigned long)(addr))) \ + PROBLEM(); \ +}) +#define MEMLEAK_FREE_TRUE(expr,addr) \ +({ \ + if((expr)) \ + MEMLEAK_FREE((addr)); \ +}) + +/* + * Sometimes, it is useful to disable memleak mapping for a specific file. + * In this case, define MEMLEAK_KILL_ALLOCATION in that file. + */ +#ifndef MEMLEAK_KILL_ALLOCATION +#define __get_free_pages(gfp_mask,gfporder) \ + MEMLEAK_WRAP(__get_free_pages,gfp_mask,gfporder) +#else +#define __get_free_pages(gfp_mask,gfporder) \ + __get_free_pages_wrap((gfp_mask),(gfporder),NULL) +#endif + +#define get_free_page(gfp_mask) \ +({ \ + unsigned long _page; \ + _page = __get_free_pages((gfp_mask),0); \ + if (_page) \ + memset((void *) _page, 0, PAGE_SIZE); \ + _page; \ +}) + +#define __get_free_page(gfp_mask) __get_free_pages((gfp_mask),0) +#define __get_dma_pages(gfp_mask, order) __get_free_pages(((gfp_mask) | GFP_DMA),(order)) +#endif /* CONFIG_MEMLEAK */ extern int low_on_memory; diff -urN ikd-ref/include/linux/proc_fs.h ikd/include/linux/proc_fs.h --- ikd-ref/include/linux/proc_fs.h Tue Oct 3 20:15:11 2000 +++ ikd/include/linux/proc_fs.h Tue Oct 3 20:15:24 2000 @@ -52,7 +52,9 @@ PROC_STRAM, PROC_SOUND, PROC_MTRR, /* whether enabled or not */ - PROC_FS + PROC_FS, + PROC_TRACE, + PROC_MEMLEAK }; enum pid_directory_inos { @@ -429,6 +431,12 @@ #endif extern struct inode_operations proc_omirr_inode_operations; extern struct inode_operations proc_ppc_htab_inode_operations; +#ifdef CONFIG_TRACE +extern struct inode_operations proc_trace_inode_operations; +#endif +#ifdef CONFIG_MEMLEAK +extern struct inode_operations proc_memleak_inode_operations; +#endif /* * generic.c diff -urN ikd-ref/include/linux/profiler.h ikd/include/linux/profiler.h --- ikd-ref/include/linux/profiler.h Thu Jan 1 01:00:00 1970 +++ ikd/include/linux/profiler.h Tue Oct 3 20:13:26 2000 @@ -0,0 +1,87 @@ +#ifndef _LINUX_PROFILER_H +#define _LINUX_PROFILER_H + +#include +#include +#include +#include + +#ifdef __KERNEL__ +#ifdef CONFIG_DEBUG_MCOUNT + +extern void mcount (void); +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(__SMP__) || 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 ikd-ref/include/linux/reboot.h ikd/include/linux/reboot.h --- ikd-ref/include/linux/reboot.h Tue Oct 3 06:33:57 2000 +++ ikd/include/linux/reboot.h Tue Oct 3 20:08:45 2000 @@ -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 ikd-ref/include/linux/sched.h ikd/include/linux/sched.h --- ikd-ref/include/linux/sched.h Tue Oct 3 20:08:37 2000 +++ ikd/include/linux/sched.h Tue Oct 3 20:15:22 2000 @@ -5,6 +5,7 @@ extern unsigned long global_event; +#include #include #include #include @@ -34,6 +35,9 @@ #define CLONE_PID 0x00001000 /* set if pid shared */ #define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */ #define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */ +#ifdef CONFIG_DEBUG_MCOUNT +#define PF_NO_MCOUNT 0x00008000 /* skip mcount() processing */ +#endif /* * These are the constant used to fake the fixed-point load-average @@ -333,6 +337,10 @@ /* oom handling */ int oom_kill_try; + +#ifdef CONFIG_DEBUG_SOFTLOCKUP + unsigned int deadlock_count; +#endif }; /* diff -urN ikd-ref/include/linux/skbuff.h ikd/include/linux/skbuff.h --- ikd-ref/include/linux/skbuff.h Tue Oct 3 19:16:26 2000 +++ ikd/include/linux/skbuff.h Tue Oct 3 20:15:22 2000 @@ -143,12 +143,30 @@ extern void skb_unlink(struct sk_buff *buf); extern __u32 skb_queue_len(struct sk_buff_head *list); 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 struct sk_buff * dev_alloc_skb(unsigned int size); extern void kfree_skbmem(struct sk_buff *skb); extern struct sk_buff * skb_clone(struct sk_buff *skb, int priority); extern struct sk_buff * skb_copy(struct sk_buff *skb, int priority); extern struct sk_buff * skb_realloc_headroom(struct sk_buff *skb, int newheadroom); +#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(struct sk_buff *skb, int priority, struct alloc_struct * IDPTR); +extern struct sk_buff * skb_realloc_headroom_wrap(struct sk_buff *skb, int newheadroom,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_realloc_headroom(skb,newheadroom) MEMLEAK_WRAP(skb_realloc_headroom,(skb),(newheadroom)) +#endif /* CONFIG_MEMLEAK */ + #define dev_kfree_skb(a) kfree_skb(a) extern unsigned char * skb_put(struct sk_buff *skb, unsigned int len); extern unsigned char * skb_push(struct sk_buff *skb, unsigned int len); @@ -548,6 +566,7 @@ kfree_skb(skb); } +#ifndef CONFIG_MEMLEAK extern __inline__ struct sk_buff *dev_alloc_skb(unsigned int length) { struct sk_buff *skb; @@ -557,6 +576,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 */ extern __inline__ struct sk_buff * skb_cow(struct sk_buff *skb, unsigned int headroom) diff -urN ikd-ref/include/linux/slab.h ikd/include/linux/slab.h --- ikd-ref/include/linux/slab.h Tue Oct 3 20:15:11 2000 +++ ikd/include/linux/slab.h Tue Oct 3 20:15:22 2000 @@ -9,6 +9,8 @@ #if defined(__KERNEL__) +#include + typedef struct kmem_cache_s kmem_cache_t; #include @@ -48,6 +50,7 @@ extern long kmem_cache_init(long, long); extern void kmem_cache_sizes_init(void); extern kmem_cache_t *kmem_find_general_cachep(size_t); +#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)); @@ -59,6 +62,33 @@ extern void *kmalloc(size_t, int); extern void kfree(const void *); extern void kfree_s(const void *, size_t); +#else +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 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 *); +extern void kfree_s(const void *, size_t); + +#define kmem_cache_create(name,size,offset,flags,ctor,dtor) \ + MEMLEAK_WRAP(kmem_cache_create,name,size,offset,flags,ctor,dtor) + +#ifndef MEMLEAK_KILL_ALLOCATION +#define kmem_cache_alloc(cachep,flags) \ + MEMLEAK_WRAP(kmem_cache_alloc,cachep,flags) +#else +#define kmem_cache_alloc(cachep,flags) \ + kmem_cache_alloc_wrap((cachep),(flags),NULL) +#endif + +#define kmalloc(size,priority) \ + MEMLEAK_WRAP(kmalloc,size,priority) + +#endif /* CONFIG_MEMLEAK */ extern void kmem_cache_reap(int); extern int get_slabinfo(char *); diff -urN ikd-ref/include/linux/smp.h ikd/include/linux/smp.h --- ikd-ref/include/linux/smp.h Tue Oct 3 20:15:11 2000 +++ ikd/include/linux/smp.h Tue Oct 3 20:15:22 2000 @@ -54,6 +54,10 @@ extern int smp_num_cpus; +#if defined(CONFIG_KDB) +extern volatile unsigned long smp_kdb_wait; +extern void smp_kdb_stop(int); +#endif /* CONFIG_KDB */ extern volatile unsigned long smp_msg_data; extern volatile int smp_src_cpu; extern volatile int smp_msg_id; diff -urN ikd-ref/include/linux/sysctl.h ikd/include/linux/sysctl.h --- ikd-ref/include/linux/sysctl.h Tue Oct 3 20:08:37 2000 +++ ikd/include/linux/sysctl.h Tue Oct 3 20:08:45 2000 @@ -104,6 +104,7 @@ KERN_MSGMNB=36, /* int: Maximum message queue size */ KERN_MSGPOOL=37, /* int: Maximum system message pool size */ KERN_SYSRQ=38, /* int: Sysreq enable */ + KERN_KMSGDUMP=39, /* string: kmsgdump flags */ KERN_SHMALL=41, /* int: maximum size of shared memory */ KERN_SPARC_STOP_A=44, /* int: Sparc Stop-A enable */ }; @@ -428,6 +429,10 @@ }; /* CTL_DEBUG names: */ +enum { + DEBUG_KSTACK_METER = 1, + DEBUG_DISABLE_MCOUNT = 2, +}; /* CTL_DEV names: */ enum { diff -urN ikd-ref/include/linux/vmalloc.h ikd/include/linux/vmalloc.h --- ikd-ref/include/linux/vmalloc.h Tue Oct 3 20:15:11 2000 +++ ikd/include/linux/vmalloc.h Tue Oct 3 20:16:37 2000 @@ -13,12 +13,26 @@ struct vm_struct * next; }; +#ifndef CONFIG_MEMLEAK struct vm_struct * get_vm_area(unsigned long size); +#endif void vfree(void * addr); +#ifndef CONFIG_MEMLEAK void * vmalloc(unsigned long size); +#endif long vread(char *buf, char *addr, unsigned long count); void vmfree_area_pages(unsigned long address, unsigned long size); +#ifndef CONFIG_MEMLEAK int vmalloc_area_pages(unsigned long address, unsigned long size); +#else /* CONFIG_MEMLEAK */ +extern void * vmalloc_wrap(unsigned long size, struct alloc_struct *id); +extern struct vm_struct * get_vm_area_wrap(unsigned long size, struct alloc_struct *id); +extern int vmalloc_area_pages_wrap(unsigned long address, unsigned long size, struct alloc_struct *id); + +#define vmalloc(size) MEMLEAK_WRAP(vmalloc,size) +#define get_vm_area(size) MEMLEAK_WRAP(get_vm_area,size) +#define vmalloc_area_pages(address, size) MEMLEAK_WRAP(vmalloc_area_pages,address,size) +#endif /* CONFIG_MEMLEAK */ extern inline void * vmalloc_32(unsigned long size) { return vmalloc(size); diff -urN ikd-ref/include/net/sock.h ikd/include/net/sock.h --- ikd-ref/include/net/sock.h Tue Oct 3 16:36:19 2000 +++ ikd/include/net/sock.h Tue Oct 3 20:15:22 2000 @@ -710,10 +710,13 @@ 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); extern void destroy_sock(struct sock *sk); +#ifndef CONFIG_MEMLEAK extern struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, int priority); @@ -724,6 +727,29 @@ 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_wmalloc_err_wrap(struct sock *sk, + unsigned long size, int force, + int priority, int *err); +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); extern unsigned long sock_rspace(struct sock *sk); @@ -736,12 +762,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); diff -urN ikd-ref/init/main.c ikd/init/main.c --- ikd-ref/init/main.c Tue Oct 3 20:08:37 2000 +++ ikd/init/main.c Tue Oct 3 20:11:21 2000 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -58,6 +59,10 @@ extern int irda_device_init(void); +#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. @@ -405,6 +410,14 @@ extern int agp_init (void); #endif +#ifdef __sparc__ +extern int serial_console; +#endif + +#ifdef CONFIG_DEBUG_MCOUNT +extern void mcount_init(void); +#endif + /* * Boot command-line arguments */ @@ -1289,6 +1302,12 @@ console_loglevel = 10; continue; } +#if defined(CONFIG_KDB) + if (!strcmp(line,"kdb")) { + kdb_flags = KDB_FLAG_EARLYKDB; + continue; + } +#endif /* CONFIG_KDB */ if (!strncmp(line,"init=",5)) { line += 5; execute_command = line; @@ -1359,6 +1378,32 @@ extern void initialize_secondary(void); +#if defined(__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 + /* * Activate the first processor. */ @@ -1382,6 +1427,13 @@ printk(linux_banner); setup_arch(&command_line, &memory_start, &memory_end); memory_start = paging_init(memory_start,memory_end); +#ifdef CONFIG_MEMLEAK + /* + * memleak_init must run before other xx_init() will start + * eating ram. + */ + memory_end = memleak_init(memory_start,memory_end); +#endif trap_init(); memory_start = init_IRQ( memory_start ); sched_init(); @@ -1417,6 +1469,13 @@ } #endif mem_init(memory_start,memory_end); +#if defined(CONFIG_KDB) + { + extern void kdb_init(void); + + kdb_init(); + } +#endif kmem_cache_sizes_init(); #ifdef CONFIG_3215_CONSOLE con3215_activate(); @@ -1446,6 +1505,14 @@ check_bugs(); printk("POSIX conformance testing by UNIFIX\n"); +#ifdef CONFIG_DEBUG_MCOUNT + mcount_init(); +#endif + +#if defined(CONFIG_KDB) + if (kdb_flags & KDB_FLAG_EARLYKDB) + KDB_ENTER(); +#endif /* * We count on the initial thread going ok * Like idlers init is an unlocked kernel thread, which will @@ -1651,6 +1718,10 @@ (void) dup(0); (void) dup(0); +#if defined(CONFIG_KDB) + if (kdb_flags & KDB_FLAG_EARLYKDB) + KDB_ENTER(); +#endif /* * We try each of these until one succeeds. * diff -urN ikd-ref/kernel/Makefile ikd/kernel/Makefile --- ikd-ref/kernel/Makefile Mon Jan 17 16:44:49 2000 +++ ikd/kernel/Makefile Tue Oct 3 20:08:45 2000 @@ -10,6 +10,10 @@ .S.s: $(CPP) -traditional $< -o $*.s +SUB_DIRS := debug +MOD_SUB_DIRS := debug +ALL_SUB_DIRS := debug + O_TARGET := kernel.o O_OBJS = sched.o dma.o fork.o exec_domain.o panic.o printk.o sys.o \ module.o exit.o itimer.o info.o time.o softirq.o resource.o \ diff -urN ikd-ref/kernel/debug/Config.in ikd/kernel/debug/Config.in --- ikd-ref/kernel/debug/Config.in Thu Jan 1 01:00:00 1970 +++ ikd/kernel/debug/Config.in Tue Oct 3 20:08:45 2000 @@ -0,0 +1,39 @@ +# +# 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 + 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 + 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 ikd-ref/kernel/debug/Makefile ikd/kernel/debug/Makefile --- ikd-ref/kernel/debug/Makefile Thu Jan 1 01:00:00 1970 +++ ikd/kernel/debug/Makefile Tue Oct 3 20:08:45 2000 @@ -0,0 +1,17 @@ +# +# 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... + +ifeq ($(CONFIG_KERNEL_DEBUGGING),y) + O_TARGET := debug.o + OX_OBJS = profiler.o + # Must turn off profiling for the profiler. + override CFLAGS := $(CFLAGS:%-pg=%-g -c) +endif + +include $(TOPDIR)/Rules.make diff -urN ikd-ref/kernel/debug/profiler.c ikd/kernel/debug/profiler.c --- ikd-ref/kernel/debug/profiler.c Thu Jan 1 01:00:00 1970 +++ ikd/kernel/debug/profiler.c Tue Oct 3 20:08:45 2000 @@ -0,0 +1,411 @@ +/* + * 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 + +/* + * 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 = vmalloc(sizeof(*trace_table))) == NULL) { + printk("mcount_init: cannot vmalloc 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 (!(boot_cpu_data.x86_capability & 0x10)) + 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-30; + for (i=0; i<30; i++) { + j %= CONFIG_TRACE_SIZE; /* wraparound */ + t = &(trace_table->entries[j++]); + /* ksymoops expects [] */ + printk ("[<%08lx>] ", t->pc); +#ifdef CONFIG_TRACE_PID + printk("%d ", t->pid); +#endif +#if defined(CONFIG_TRACE_CPU) && defined(__SMP__) + printk("%d ", t->cpu); +#endif + } + RESUME_MCOUNT_TRACE; +} +#endif /* CONFIG_TRACE */ + +#ifdef __i386__ +/* + * 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; int __dummy[15]; }; + +#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(unsigned int eip) +{ +#define video ((short int *)(0x000b8000 + __PAGE_OFFSET)) +#define HISTORY 24 +#define ALIGN __attribute__((aligned(4))) + + int i, value; + unsigned int tmp; + + /* + * We split the codepath in a dumb way, to get speed and proper + * per-CPU execution. + */ +#ifdef __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 __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 /* __SMP__ */ + +#undef ALIGN +#undef HISTORY +#undef video +} + +#endif /* CONFIG_PRINT_EIP */ +#endif /* __i386__ */ + +#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() - sizeof(struct task_struct) /* accounted the tss -arca */ + < 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(__SMP__) + t->cpu = smp_processor_id(); +#endif + } +#endif /* CONFIG_TRACE */ + return 0; +} + +#ifdef __i386__ + +void mcount(void) +{ + int do_oops; +#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; + + LOCK_MCOUNT_TRACE(flags); + do_oops = mcount_internal((profiler_pc_t)__builtin_return_address(0)); + 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 /* __i386__ */ + +#endif /* CONFIG_DEBUG_MCOUNT */ diff -urN ikd-ref/kernel/fork.c ikd/kernel/fork.c --- ikd-ref/kernel/fork.c Tue Oct 3 20:08:37 2000 +++ ikd/kernel/fork.c Tue Oct 3 20:08:45 2000 @@ -11,12 +11,14 @@ * management can be a bitch. See 'mm/mm.c': 'copy_page_tables()' */ +#include #include #include #include #include #include #include +#include #include #include @@ -617,9 +619,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); /* diff -urN ikd-ref/kernel/ksyms.c ikd/kernel/ksyms.c --- ikd-ref/kernel/ksyms.c Tue Oct 3 20:08:37 2000 +++ ikd/kernel/ksyms.c Tue Oct 3 20:08:45 2000 @@ -100,19 +100,38 @@ EXPORT_SYMBOL(daemonize); /* internal kernel memory management */ +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(__get_free_pages); +#else +EXPORT_SYMBOL(__get_free_pages_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); EXPORT_SYMBOL(kmem_find_general_cachep); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(kmem_cache_create); +#endif EXPORT_SYMBOL(kmem_cache_shrink); EXPORT_SYMBOL(kmem_cache_destroy); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(kmem_cache_alloc); +#endif EXPORT_SYMBOL(kmem_cache_free); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(kmalloc); +#endif EXPORT_SYMBOL(kfree); EXPORT_SYMBOL(kfree_s); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(vmalloc); +#endif EXPORT_SYMBOL(vfree); EXPORT_SYMBOL(mem_map); EXPORT_SYMBOL(remap_page_range); diff -urN ikd-ref/kernel/module.c ikd/kernel/module.c --- ikd-ref/kernel/module.c Thu May 4 13:00:40 2000 +++ ikd/kernel/module.c Tue Oct 3 20:08:45 2000 @@ -6,6 +6,9 @@ #include #include #include +#if defined(CONFIG_KDB) +#include +#endif /* * Originally by Anonymous (as far as I know...) @@ -170,6 +173,9 @@ long namelen, n_namelen, i, error = -EPERM; unsigned long mod_user_size; struct module_ref *dep; +#if defined(CONFIG_KDB) + struct module_symbol *s; +#endif lock_kernel(); if (!capable(CAP_SYS_MODULE)) @@ -340,6 +346,11 @@ } atomic_dec(&mod->uc.usecount); +#if defined(CONFIG_KDB) + for(i=0,s=mod->syms; insyms; i++, s++){ + kdbaddmodsym(s->name, s->value); + } +#endif /* And set it running. */ mod->flags = (mod->flags | MOD_RUNNING) & ~MOD_INITIALIZING; error = 0; @@ -778,6 +789,9 @@ { struct module_ref *dep; unsigned i; +#if defined(CONFIG_KDB) + struct module_symbol *s; +#endif /* Let the module clean up. */ @@ -788,6 +802,15 @@ mod->cleanup(); mod->flags &= ~MOD_RUNNING; } + +#if defined(CONFIG_KDB) + /* + * Remove symbols from kernel debugger + */ + for(i=0,s=mod->syms; insyms; i++, s++){ + kdbdelmodsym(s->name); + } +#endif /* Remove the module from the dependency lists. */ diff -urN ikd-ref/kernel/panic.c ikd/kernel/panic.c --- ikd-ref/kernel/panic.c Thu May 4 13:00:40 2000 +++ ikd/kernel/panic.c Tue Oct 3 20:08:45 2000 @@ -15,6 +15,13 @@ #include #include #include +#ifdef CONFIG_TRACE +#include +#endif + +#if defined(__i386__) && defined(CONFIG_KMSGDUMP) +extern void machine_dump(int); +#endif asmlinkage void sys_sync(void); /* it's really int */ extern void unblank_console(void); @@ -38,6 +45,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); @@ -55,6 +65,24 @@ smp_send_stop(); #endif +#if defined(__i386__) && defined(CONFIG_KMSGDUMP) + /* We allow SysRq to be used for seconds, and + then the dump is forced. This way, if IRQs are frozen and + keyboard doesn't respond anymore, the system can dump messages + and reboot itself. + This code has been voluntarily inserted before notifier_call_chain + to ensure proper execution in case of crash. Please tell me if this + can be wrong. + */ + printk(KERN_EMERG "Dumping messages in %d seconds : last chance for Alt-SysRq...", + panic_timeout); + sti(); + for(panic_timeout*=10; panic_timeout>0; panic_timeout--) { + CHECK_EMERGENCY_SYNC; + mdelay(100); + } + machine_dump(0); /* 0 tells machine_dump that it's called from panic() */ +#endif notifier_call_chain(&panic_notifier_list, 0, NULL); if (panic_timeout > 0) diff -urN ikd-ref/kernel/printk.c ikd/kernel/printk.c --- ikd-ref/kernel/printk.c Mon Jan 17 16:44:50 2000 +++ ikd/kernel/printk.c Tue Oct 3 20:08:45 2000 @@ -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 @@ -241,6 +243,75 @@ 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); + } +} + +#if defined(__i386__) && defined(CONFIG_KMSGDUMP) +/* + * dump_syslog() : Added on 19991020 by Willy Tarreau + * This function is only used by machine_dump. It basically does the same + * as do_syslog(3), but avoids to check if *buf is writeable because this + * sometimes made the system crash in case of real instability (common + * case when machine_dump is called). + */ + +void dump_syslog(char *buf) { + unsigned long i, j, count; + + if (!buf) + return; + /* + * The logged_chars, log_start, and log_size values may + * change from an interrupt, so we disable interrupts. + */ + spin_lock_irq(&console_lock); + count = LOG_BUF_LEN; + if (count > logged_chars) + count = logged_chars; + j = log_start + log_size - count; + spin_unlock_irq(&console_lock); + for (i = 0; i < count; i++) { + buf[i] = *((char *) log_buf+(j++ & (LOG_BUF_LEN-1))); + } +} +#endif asmlinkage int sys_syslog(int type, char * buf, int len) { diff -urN ikd-ref/kernel/sched.c ikd/kernel/sched.c --- ikd-ref/kernel/sched.c Tue Oct 3 20:08:37 2000 +++ ikd/kernel/sched.c Tue Oct 3 20:08:45 2000 @@ -24,6 +24,11 @@ * current-task */ +/* + * Semaphore deadlock detector. Copyright (C) 1999 Andrea Arcangeli + */ + +#include #include #include #include @@ -39,6 +44,7 @@ #include #include +#include /* * kernel variables @@ -792,6 +798,9 @@ __schedule_tail(prev); same_process: +#ifdef CONFIG_DEBUG_SOFTLOCKUP + prev->deadlock_count=CONFIG_SOFTLOCKUP_THRESHOLD; +#endif reacquire_kernel_lock(current); return; @@ -975,14 +984,37 @@ tsk->state = TASK_RUNNING; \ remove_wait_queue(&sem->wait, &wait); +#ifdef CONFIG_SEMAPHORE_DEADLOCK +static void generate_oops (struct semaphore *sem) +{ + sema_init(sem, 9876); + wake_up(&sem->wait); +} +#endif + void __down(struct semaphore * sem) { DOWN_VAR +#ifdef CONFIG_SEMAPHORE_DEADLOCK + struct timer_list timer; + init_timer (&timer); + timer.expires = jiffies + HZ*20; + timer.data = (unsigned long) sem; + timer.function = (void (*)(unsigned long)) generate_oops; + add_timer(&timer); +#endif DOWN_HEAD(TASK_UNINTERRUPTIBLE) if (waking_non_zero(sem)) break; schedule(); +#ifdef CONFIG_SEMAPHORE_DEADLOCK + if (atomic_read(&sem->count) == 9876) + *(int *) 0 = 0; +#endif DOWN_TAIL(TASK_UNINTERRUPTIBLE) +#ifdef CONFIG_SEMAPHORE_DEADLOCK + del_timer(&timer); +#endif } int __down_interruptible(struct semaphore * sem) diff -urN ikd-ref/kernel/softirq.c ikd/kernel/softirq.c --- ikd-ref/kernel/softirq.c Mon Jan 17 16:44:50 2000 +++ ikd/kernel/softirq.c Tue Oct 3 20:08:45 2000 @@ -11,10 +11,12 @@ * due bh_mask_count not atomic handling. Copyright (C) 1998 Andrea Arcangeli */ +#include #include #include #include #include +#include #include @@ -66,5 +68,8 @@ hardirq_endlock(cpu); } softirq_endlock(cpu); +#if defined(CONFIG_DEBUG_SOFTLOCKUP) && (defined(__SMP__) || defined(CONFIG_SMP)) + mcount(); +#endif } } diff -urN ikd-ref/kernel/sys.c ikd/kernel/sys.c --- ikd-ref/kernel/sys.c Thu May 4 13:00:40 2000 +++ ikd/kernel/sys.c Tue Oct 3 20:08:45 2000 @@ -144,6 +144,37 @@ return max_prio; } +/* 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, @@ -207,6 +238,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 ikd-ref/kernel/sysctl.c ikd/kernel/sysctl.c --- ikd-ref/kernel/sysctl.c Tue Oct 3 20:08:37 2000 +++ ikd/kernel/sysctl.c Tue Oct 3 20:08:45 2000 @@ -51,6 +51,10 @@ extern int shmall_max; #endif +#if defined(__i386__) && defined(CONFIG_KMSGDUMP) +extern char kmsgdump_flags[]; +#endif + #ifdef __sparc__ extern char reboot_command []; extern int stop_a_enabled; @@ -231,6 +235,10 @@ {KERN_SYSRQ, "sysrq", &sysrq_enabled, sizeof (int), 0644, NULL, &proc_dointvec}, #endif +#if defined(__i386__) && defined(CONFIG_KMSGDUMP) + {KERN_KMSGDUMP, "kmsgdump", &kmsgdump_flags, 16, + 0644, NULL, &proc_dostring, &sysctl_string }, +#endif {0} }; @@ -285,7 +293,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} }; diff -urN ikd-ref/mm/Makefile ikd/mm/Makefile --- ikd-ref/mm/Makefile Tue Oct 3 20:08:37 2000 +++ ikd/mm/Makefile Tue Oct 3 20:08:45 2000 @@ -16,4 +16,12 @@ O_OBJS += bigmem.o endif +ifeq ($(CONFIG_MEMLEAK),y) +O_OBJS += memleak.o +endif + include $(TOPDIR)/Rules.make + +# memleak beats up cache bad enough without mcount() helping. +memleak.o : memleak.c + $(CC) $(CFLAGS:%-pg=%) -c -o $@ $< diff -urN ikd-ref/mm/memleak.c ikd/mm/memleak.c --- ikd-ref/mm/memleak.c Thu Jan 1 01:00:00 1970 +++ ikd/mm/memleak.c Tue Oct 3 20:08:45 2000 @@ -0,0 +1,265 @@ +/* + * linux/mm/memleak.c, memory leak detector + * + * Copyright (C) 1997 Ingo Molnar + * + * Maintainer: Mike Galbraith mikeg@weiden.de + * + * Changelog: + * Dec 18 1997: memleak.c - added wrapper functions for slab.c + * Dec 18 1997: include/asm-i386/pgtable.h + * converted pte_alloc_kernel, pte_alloc, pgd_alloc to macros + */ + +#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 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 + */ +unsigned long memleak_init (unsigned long start_mem, unsigned long end_mem) +{ + unsigned long MEMSIZE, size; + + id_map[0] = &NULL_id; + + end_mem = PAGE_ALIGN(end_mem); + + MEMSIZE = end_mem-PAGE_OFFSET; + ENTRIES = MEMSIZE/(MIN_MEMBLOCK_SIZE+sizeof(struct alloc_entry))+1; + + size = ENTRIES * sizeof(struct alloc_entry); + + end_mem = PAGE_ALIGN(end_mem-size)-PAGE_SIZE; + + alloc_map = (struct alloc_entry *) end_mem; + + printk("MEMLEAK, allocating %ld KB allocation map.\n", size/1024); + + if (!alloc_map) { + PROBLEM(); + for(;;); + } + + memset(alloc_map,0,size); + curr_id = 1; + + return end_mem; +} + + +#define LINE_SIZE 128 + +static ssize_t read_allocations (struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + struct alloc_struct * id; + unsigned long p; + unsigned long idx; + unsigned long timestamp; + + char line [LINE_SIZE]; + + if(ppos != &file->f_pos) + return -ESPIPE; + + p = idx = *ppos; + + if (count < 0) + return -EINVAL; +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); + copy_to_user(buf,line,count); + } else { + if (!idx) { + count = sprintf(line,"<%p> jiffies.c:%d (%ld)\n", NULL, 0, jiffies/HZ); + copy_to_user(buf,line,count); + } else { + idx++; + *ppos = idx; + /* + * there is at least one allocation in the system + */ + goto repeat; + } + } + + idx++; + *ppos = idx; + + return count; +} + +static struct file_operations proc_memleak_operations = { + NULL, /* lseek */ + read_allocations, + NULL, /* write */ +}; + +struct inode_operations proc_memleak_inode_operations = { + &proc_memleak_operations, +}; diff -urN ikd-ref/mm/page_alloc.c ikd/mm/page_alloc.c --- ikd-ref/mm/page_alloc.c Tue Oct 3 20:08:37 2000 +++ ikd/mm/page_alloc.c Tue Oct 3 20:08:45 2000 @@ -6,6 +6,7 @@ * Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999 */ +#define MEMLEAK_PASS_ALLOCATION #include #include #include @@ -102,6 +103,7 @@ #define list(x) (mem_map+(x)) #define __free_pages_ok(map_nr, mask, area, index) \ nr_free_pages -= (mask); \ + MEMLEAK_FREE(PAGE_OFFSET + ((map_nr) << PAGE_SHIFT)); /*ADDRESS macro below*/ \ while ((mask) + (1 << (NR_MEM_LISTS-1))) { \ if (!test_and_change_bit((index), (area)->map)) \ break; \ @@ -225,6 +227,7 @@ UPDATE_NR_FREE_BIGPAGES(map_nr, order); \ area->count--; \ EXPAND(ret, map_nr, order, new_order, area); \ + MEMLEAK_ALLOC_NOLOCK(ADDRESS(map_nr)); \ spin_unlock_irqrestore(&page_alloc_lock, flags); \ return ADDRESS(map_nr); \ } \ @@ -264,7 +267,11 @@ } } +#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 { unsigned long flags; diff -urN ikd-ref/mm/slab.c ikd/mm/slab.c --- ikd-ref/mm/slab.c Tue Oct 3 06:14:24 2000 +++ ikd/mm/slab.c Tue Oct 3 20:08:45 2000 @@ -100,10 +100,19 @@ * is less than 512 (PAGE_SIZE<<3), but greater than 256. */ +#define MEMLEAK_PASS_ALLOCATION +#define MEMLEAK_UNWRAP_SLAB + #include #include #include #include +#include +#include + +#include +#include +#include /* If there is a different PAGE_SIZE around, and it works with this allocator, * then change the following. @@ -676,9 +685,15 @@ * NOTE: The 'name' is assumed to be memory that is _not_ going to disappear. */ kmem_cache_t * +#ifndef CONFIG_MEMLEAK 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_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: "; kmem_cache_t *searchp; @@ -1041,6 +1056,7 @@ ret = 1; if (cachep->c_lastp == kmem_slab_end(cachep)) ret--; /* Cache is empty. */ + MEMLEAK_FREE_TRUE((ret == 0),cachep); spin_unlock_irq(&cachep->c_spinlock); return ret; } @@ -1112,7 +1128,11 @@ /* Get the memory for a slab management obj. */ static inline kmem_slab_t * +#ifndef CONFIG_MEMLEAK kmem_cache_slabmgmt(kmem_cache_t *cachep, void *objp, int local_flags) +#else +kmem_cache_slabmgmt_wrap(kmem_cache_t *cachep, void *objp, int local_flags, struct alloc_struct *IDPTR) +#endif { kmem_slab_t *slabp; @@ -1200,7 +1220,11 @@ * kmem_cache_alloc() when there are no active objs left in a cache. */ static int +#ifndef CONFIG_MEMLEAK kmem_cache_grow(kmem_cache_t * cachep, int flags) +#else +kmem_cache_grow_wrap(kmem_cache_t * cachep, int flags, struct alloc_struct *IDPTR) +#endif { kmem_slab_t *slabp; struct page *page; @@ -1410,7 +1434,11 @@ /* Returns a ptr to an obj in the given cache. */ static inline void * +#ifndef CONFIG_MEMLEAK __kmem_cache_alloc(kmem_cache_t *cachep, int flags) +#else +__kmem_cache_alloc_wrap(kmem_cache_t *cachep, int flags, struct alloc_struct *IDPTR) +#endif { kmem_slab_t *slabp; kmem_bufctl_t *bufp; @@ -1448,6 +1476,7 @@ * obj has been removed from the slab. Should be safe to drop * the lock here. */ + MEMLEAK_ALLOC_NOLOCK(objp); spin_unlock_irqrestore(&cachep->c_spinlock, save_flags); #if SLAB_DEBUG_SUPPORT if (cachep->c_flags & SLAB_RED_ZONE) @@ -1580,6 +1609,7 @@ kmem_poison_obj(cachep, objp); } #endif /* SLAB_DEBUG_SUPPORT */ + MEMLEAK_FREE(objp); spin_unlock_irqrestore(&cachep->c_spinlock, save_flags); return; } @@ -1655,7 +1685,11 @@ } void * +#ifndef CONFIG_MEMLEAK kmem_cache_alloc(kmem_cache_t *cachep, int flags) +#else +kmem_cache_alloc_wrap(kmem_cache_t *cachep, int flags, struct alloc_struct *IDPTR) +#endif { return __kmem_cache_alloc(cachep, flags); } @@ -1667,7 +1701,11 @@ } void * +#ifndef CONFIG_MEMLEAK kmalloc(size_t size, int flags) +#else +kmalloc_wrap(size_t size, int flags, struct alloc_struct *IDPTR) +#endif { cache_sizes_t *csizep = cache_sizes; diff -urN ikd-ref/mm/vmalloc.c ikd/mm/vmalloc.c --- ikd-ref/mm/vmalloc.c Tue Oct 3 20:08:37 2000 +++ ikd/mm/vmalloc.c Tue Oct 3 20:08:45 2000 @@ -5,13 +5,56 @@ * Support of BIGMEM added by Gerhard Wichert, Siemens AG, July 1999 */ +#define MEMLEAK_PASS_ALLOCATION +#define MEMLEAK_UNWRAP_VMALLOC +#define MEMLEAK_UNWRAP_SLAB + +#include #include #include +#include #include static struct vm_struct * vmlist = NULL; +#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; + + 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; + } + } + return 0; +} +#endif + static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size) { pte_t * pte; @@ -83,7 +126,12 @@ flush_tlb_all(); } +#ifndef CONFIG_MEMLEAK static inline int alloc_area_pte(pte_t * pte, unsigned long address, unsigned long size) +#else +static inline int alloc_area_pte_wrap(pte_t * pte, unsigned long address, + unsigned long size, struct alloc_struct *IDPTR) +#endif { unsigned long end; @@ -105,7 +153,12 @@ return 0; } +#ifndef CONFIG_MEMLEAK static inline int alloc_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size) +#else +static inline int alloc_area_pmd_wrap(pmd_t * pmd, unsigned long address, + unsigned long size, struct alloc_struct *IDPTR) +#endif { unsigned long end; @@ -125,7 +178,12 @@ return 0; } +#ifndef CONFIG_MEMLEAK int vmalloc_area_pages(unsigned long address, unsigned long size) +#else +int vmalloc_area_pages_wrap(unsigned long address, unsigned long size, + struct alloc_struct *IDPTR ) +#endif { pgd_t * dir; unsigned long end = address + size; @@ -150,7 +208,11 @@ return 0; } +#ifndef CONFIG_MEMLEAK struct vm_struct * get_vm_area(unsigned long size) +#else +struct vm_struct * get_vm_area_wrap(unsigned long size, struct alloc_struct *IDPTR) +#endif { unsigned long addr; struct vm_struct **p, *tmp, *area; @@ -196,7 +258,11 @@ printk("Trying to vfree() nonexistent vm area (%p)\n", addr); } +#ifndef CONFIG_MEMLEAK void * vmalloc(unsigned long size) +#else +void * vmalloc_wrap(unsigned long size, struct alloc_struct *IDPTR) +#endif { void * addr; struct vm_struct *area; diff -urN ikd-ref/net/core/skbuff.c ikd/net/core/skbuff.c --- ikd-ref/net/core/skbuff.c Mon Jan 17 16:44:50 2000 +++ ikd/net/core/skbuff.c Tue Oct 3 20:08:45 2000 @@ -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 @@ -61,6 +65,8 @@ #include #include +#include + /* * Skb list spinlock */ @@ -116,7 +122,12 @@ * */ +#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; @@ -232,7 +243,12 @@ * Duplicate an sk_buff. The new one is not owned by a socket. */ +#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; @@ -261,7 +277,12 @@ * This is slower, and copies the whole data area */ +#ifndef CONFIG_MEMLEAK struct sk_buff *skb_copy(struct sk_buff *skb, int gfp_mask) +#else +struct sk_buff *skb_copy_wrap(struct sk_buff *skb, int gfp_mask, + struct alloc_struct * IDPTR) +#endif { struct sk_buff *n; unsigned long offset; @@ -310,7 +331,12 @@ return n; } +#ifndef CONFIG_MEMLEAK struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, int newheadroom) +#else +struct sk_buff *skb_realloc_headroom_wrap(struct sk_buff *skb, + int newheadroom, struct alloc_struct * IDPTR) +#endif { struct sk_buff *n; unsigned long offset; diff -urN ikd-ref/net/core/sock.c ikd/net/core/sock.c --- ikd-ref/net/core/sock.c Tue Oct 3 06:14:24 2000 +++ ikd/net/core/sock.c Tue Oct 3 20:08:45 2000 @@ -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 @@ -130,6 +135,8 @@ #include #endif +#include + #define min(a,b) ((a)<(b)?(a):(b)) struct linux_mib net_statistics; @@ -485,7 +492,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); @@ -555,10 +566,14 @@ } +#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); @@ -601,10 +616,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); @@ -621,10 +640,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 (atomic_read(&sk->omem_alloc)+size < sysctl_optmem_max) { void *mem; @@ -712,8 +735,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 ikd-ref/net/netsyms.c ikd/net/netsyms.c --- ikd-ref/net/netsyms.c Tue Oct 3 06:14:25 2000 +++ ikd/net/netsyms.c Tue Oct 3 20:08:45 2000 @@ -126,10 +126,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_dup); EXPORT_SYMBOL(sock_no_release); @@ -149,18 +156,29 @@ EXPORT_SYMBOL(sock_no_recvmsg); 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(sock_rspace); 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_realloc_headroom); +#endif EXPORT_SYMBOL(datagram_poll); EXPORT_SYMBOL(put_cmsg); EXPORT_SYMBOL(net_families); +#ifndef CONFIG_MEMLEAK EXPORT_SYMBOL(sock_kmalloc); +#else +EXPORT_SYMBOL(sock_kmalloc_wrap); +#endif EXPORT_SYMBOL(sock_kfree_s); EXPORT_SYMBOL(skb_queue_lock); @@ -485,10 +503,19 @@ EXPORT_SYMBOL(fddi_setup); #endif /* CONFIG_FDDI */ EXPORT_SYMBOL(eth_copy_and_sum); +#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_realloc_headroom_wrap); +#endif EXPORT_SYMBOL(netif_rx); EXPORT_SYMBOL(dev_add_pack); EXPORT_SYMBOL(dev_remove_pack); diff -urN ikd-ref/scripts/Makefile ikd/scripts/Makefile --- ikd-ref/scripts/Makefile Mon Jan 17 16:44:52 2000 +++ ikd/scripts/Makefile Tue Oct 3 20:08:45 2000 @@ -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,7 +47,11 @@ tkparse.o tkcond.o tkgen.o: $(HOSTCC) $(HOSTCFLAGS) -c -o $@ $(@:.o=.c) +ktrace: ktrace.o + $(CC) -o ktrace ktrace.o + clean: rm -f *~ kconfig.tk *.o tkparse mkdep split-include + rm -f ktrace include $(TOPDIR)/Rules.make diff -urN ikd-ref/scripts/genkdbsym.awk ikd/scripts/genkdbsym.awk --- ikd-ref/scripts/genkdbsym.awk Thu Jan 1 01:00:00 1970 +++ ikd/scripts/genkdbsym.awk Tue Oct 3 20:08:45 2000 @@ -0,0 +1,21 @@ +BEGIN { + printf("#include \n"); + printf("#include \"ksym.h\"\n\n"); + printf("#define KDBMAXSYMTABSIZE 7900\n"); + printf("\n__ksymtab_t __attribute__ ((section(\"kdbsymtab\"))) __kdbsymtab[KDBMAXSYMTABSIZE] = {\n"); + symcount = 0 + printf("/* Generated file */\n") > "ksym.h"; + printf("char __attribute__ ((section(\"kdbstrings\"))) kdb_null[]=\"\";\n") >> "ksym.h"; + } + + { symname = sprintf("_kdbs%d", symcount); + printf("{%s, 0x%s},\n", symname, $1); + printf("char __attribute__ ((section(\"kdbstrings\"))) %s[] = \"%s\";\n", symname, $3) >> "ksym.h"; + symcount = symcount + 1 + } + +END { + printf(" [%d ... KDBMAXSYMTABSIZE-1] = {kdb_null, 0xf}};\n", symcount); + printf("int __attribute__ ((section(\"kdbsymtab\"))) __kdbsymtabsize = %d;\n", symcount); + printf("int __attribute__ ((section(\"kdbsymtab\"))) __kdbmaxsymtabsize = KDBMAXSYMTABSIZE;\n"); + } diff -urN ikd-ref/scripts/ktrace.c ikd/scripts/ktrace.c --- ikd-ref/scripts/ktrace.c Thu Jan 1 01:00:00 1970 +++ ikd/scripts/ktrace.c Tue Oct 3 20:08:45 2000 @@ -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(__SMP__) || 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.39 usecs.\n"); + min_latency = 0.39*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 ikd-ref/scripts/make_System_map.pl ikd/scripts/make_System_map.pl --- ikd-ref/scripts/make_System_map.pl Thu Jan 1 01:00:00 1970 +++ ikd/scripts/make_System_map.pl Tue Oct 3 20:08:45 2000 @@ -0,0 +1,324 @@ +#!/usr/bin/perl -w + +# A quick and dirty prototype which will be recoded in C later. I'd +# like people to hit on this and mail kaos@ocs.com.au with any bugs, +# not the kernel list please. Mon Sep 22 03:18:11 EST 1997 + +# Current oops processing suffers from some problems. +# * ksymoops is written in C++ (the only program in the kernel that +# is). +# * ksymoops only reads System.map so it has no idea where modules are. +# * klogd has not handled loaded modules on 2.1.x for some time (cannot +# seek /dev/kmem). +# * klogd cannot always decode the oops, some errors are so bad that +# klogd is effectively dead. +# * Even when klogd can run, /proc/ksyms only shows exported symbols +# for modules so klogd cannot resolve addresses for static procs in +# modules. In contrast, System.map shows all procs, both static and +# extern. +# * Decoding an oops on a machine other than the one it occurred on is +# awkward, due to assumptions about where files are located. + +# To overcome these problems, this program reads System.map or +# equivalent, /proc/ksyms or equivalent and the raw modules. It then +# outputs a new, dynamic System.map containing the symbols from the +# input map plus all the symbols from the currently loaded modules, +# adjusted to where the modules were loaded. This map can then be fed +# into your favourite oops decoder. + +# Typical invocation: +# +# make_System_map.pl /proc/ksyms /System.map \ +# /lib/modules/`uname -r`/*/*.o > /tmp/map +# /usr/src/linux/scripts/ksymoops /tmp/map < oops_log + +# Any filename ending in '.o' is assumed to be a module and is run +# through the nm command to get its symbols. Any file whose first line +# is 8 hex digits, space, any character, space is a system map, no +# matter what its file name. Any file whose first line is 8 hex +# digits, space, any character, non-space is ksyms output, no matter +# what its file name. This means that you can feed any System.map and +# /proc/ksyms or a copy of /proc/ksyms without worrying about the file +# names. You are in control, this program makes no guesses about where +# the various files are. + +# Known bugs (and it's only release 0.1 :). +# * If the same name appears more than once with different types, all +# but one is lost. + +require 5; +use strict; +use File::Basename; + +my $nm = "/usr/bin/nm -p"; # In case somebody moves nm +my $filename; +my $errors = 0; + +# Keyed on the name the module was loaded as (blank for kernel proper). +# Contains symbol (keyed) which contains offset. +my %ksym = (); + +# Keyed on symbol. Contains offset, type. +my %map = (); + +# Keyed on basename. Contains symbol (keyed) which contains offset, type. +my %module = (); + +# Keyed on the name the module was loaded as. Contains basename of the +# real module, adjustments for .text, .data, .rodata. +my %loaded_as = (); + +# Because you can "insmod -o xxx module.o", the module name in ksyms is +# not the real key. The only way (?) is to run all the modules and +# extract externally visible symbols then pick unique symbols for each +# module. Keyed on symbol, contains object basename. +my %unique = (); + +# Convert a data type from nm to the index of the section adjustment in +# loaded_as and the section name. +my %convert_type_to_section = ( + 'T' => [1, ".text"], + 't' => [1, ".text"], + 'D' => [2, ".data"], + 'd' => [2, ".data"], + 'R' => [3, ".rodata"], + 'r' => [3, ".rodata"], + ); + +foreach $filename (@ARGV) { + if ($filename =~ /\.o$/) { + # Skip for now + } + else { + open(INPUT, $filename) || die("Cannot open $filename for input - $!\n"); + my $line = ; + $line = "****** EMPTY FILE ******" if (!defined($line)); + if ($line =~ /^[0-9a-fA-F]{8} . /) { + read_system_map($filename, $line); + } + elsif ($line =~ /^[0-9a-fA-F]{8} .[^ ]/) { + read_ksyms($filename, $line); + } + else { + print STDERR <[0]; + $ksym_offset = $ksym_kernel{$_}; + if ($map_offset != $ksym_offset) { + if ($errors++ == 0) { + print STDERR "Mismatch detected between map and ksym\n"; + } + printf STDERR "Symbol %s, map %x, ksym %x\n", $_, $map_offset, $ksym_offset; + } + } + } + die("Please feed me a matching set of System.map and /proc/ksyms\n") if ($errors); +} + +if (keys(%ksym) == 1) { + print STDERR "No modules loaded, skipping module scan\n"; +} +else { + # Extract symbols from all modules + foreach $filename (@ARGV) { + if ($filename =~ /\.o$/) { + read_object($filename); + } + } + + # Pick the unique external symbols in all modules + { + my %really_unique = (); + foreach (keys(%unique)) { + if ($unique{$_} !~ / /) { + $really_unique{$_} = $unique{$_}; + } + } + %unique = %really_unique; + } + + # Resolve the name under which each module was loaded to the module + # basename and calculate adjustments for each loaded occurrence. + resolve_loaded_as_to_basename(); + + # For every module that was loaded, add all the symbols to the map + # with suitable adjustments. We have a complete map at last! + add_loaded_modules_to_map(); +} + +# And print. +{ + my $symbol; + my @address_order = (); + foreach $symbol (keys(%map)) { + push(@address_order, [ $map{$symbol}->[0], $map{$symbol}->[1], $symbol ]); + } + sub by_address { $a->[0] <=> $b->[0] }; + my @final = sort by_address (@address_order); + foreach (@final) { + printf "%08x %s %s\n", $_->[0], $_->[1], $_->[2]; + } +} + +exit 0; # That was easy :) + +sub read_object +{ + my ($filename) = @_; + my ($offset, $type, $symbol, $external); + my $basename = basename($filename, ".o"); + if (defined($module{$basename})) { + die("Module $basename appears more than once\n"); + } + my %symbol; + $module{$basename} = \%symbol; + if (! open(NMDATA, "$nm $filename|")) { + die("$nm $filename failed $!\n"); + } + while () { + # Ignore entries with no offset (unresolved external references) + next if (substr($_, 0, 1) eq " "); + chop; + ($offset, $type, $symbol) = split(' ', $_, 3); + $offset = hex($offset); + # Module symbols are externally visible if they are type 'D' + # (global variable, initialised), 'R' (global data, read only), + # 'T' (global label/procedure), or there is a '? __ksymtab_' + # entry for the symbol. + $external = ""; + if ($type eq 'D' || $type eq 'R' || $type eq 'T') { + $external = $symbol; + } + elsif ($type eq '?' && substr($symbol, 0, 10) eq "__ksymtab_") { + $external = substr($symbol, 10); + } + if ($external ne "") { + if (!defined($unique{$external})) { + $unique{$external} = $basename; + } + elsif ($unique{$external} ne $basename) { + # Not unique after all + $unique{$external} .= " $basename"; + } + } + $symbol{$symbol} = [ $offset, $type ]; + } + close(NMDATA); +} + +sub resolve_loaded_as_to_basename +{ + my ($loaded_as, $symbol, $basename, $adjustment, $type, + $section, $section_name); + foreach $loaded_as (keys(%loaded_as)) { + foreach $symbol (keys(%{$ksym{$loaded_as}})) { + if (defined($unique{$symbol})) { + # Symbol from ksyms uniquely identifies a module. + # Calculate the adjustment and log the real module + # name. + $basename = $unique{$symbol}; + $adjustment = $ksym{$loaded_as}->{$symbol} - + $module{$basename}->{$symbol}->[0]; + $type = $module{$basename}->{$symbol}->[1]; + # Silently ignore type 'C'. It is .bss data and the first + # field is a size, not an offset. + next if ($type eq 'C'); + if (defined($convert_type_to_section{$type})) { + ($section, $section_name) = @{$convert_type_to_section{$type}}; + } + else { + die("Unexpected type $type, symbol $symbol, module $basename\n"); + } + if (!defined($loaded_as{$loaded_as})) { + $loaded_as{$loaded_as} = [ $basename, undef, undef, undef ]; + } + my $ref_resolved = $loaded_as{$loaded_as}; + if (!defined($ref_resolved->[$section])) { + $ref_resolved->[$section] = $adjustment; + printf STDERR "%s %s resolved to %s, adjustment %x\n", + $loaded_as, $section_name, $basename, $adjustment; + printf STDERR " Unique symbol %s was used, type %s\n", $symbol, $type; + } + elsif ($adjustment != $ref_resolved->[$section]) { + if ($errors++ == 0) { + printf STDERR "Mismatch detected between ksyms and %s (%s)\n", + $loaded_as, $basename; + } + printf STDERR "Symbol %s, type %s, section %s, adjustment 1 %x, adjustment 2 %x\n", + $symbol, $type, $section_name, $ref_resolved->[$section], $adjustment; + } + } + } + die("Unable to resolve $loaded_as back to the real module\n") if (!defined($adjustment)); + die("Please feed me a matching set of modules and ksyms\n") if ($errors); + } +} + +sub add_loaded_modules_to_map +{ + my ($loaded_as, $basename, $symbol, $offset, $type, $adjustment, $section); + foreach $loaded_as (keys(%loaded_as)) { + $basename = $loaded_as{$loaded_as}->[0]; + foreach $symbol (keys(%{$module{$basename}})) { + ($offset, $type) = @{$module{$basename}->{$symbol}}; + if (defined($convert_type_to_section{$type})) { + $section = $convert_type_to_section{$type}->[0]; + $adjustment = $loaded_as{$loaded_as}->[$section]; + if (defined($adjustment)) { + $map{$symbol} = [ $offset+$adjustment, $type ]; + } + } + } + } +} + +sub read_ksyms +{ + my ($filename, $line) = @_; + my ($offset, $symbol, $loaded_as); + do { + chop($line); + ($offset, $symbol, $loaded_as) = split(' ', $line, 3); + $offset = hex($offset); + if (defined($loaded_as)) { + $loaded_as =~ s:\[::; + $loaded_as =~ s:\]::; + $loaded_as{$loaded_as} = undef; # fill it in later + } + else { + $loaded_as = ""; + } + if (!defined($ksym{$loaded_as})) { + $ksym{$loaded_as} = {}; + } + $ksym{$loaded_as}->{$symbol} = $offset; + } while ($line = ); +} + +sub read_system_map +{ + my ($filename, $line) = @_; + my ($offset, $type, $symbol); + do { + chop($line); + ($offset, $type, $symbol) = split(' ', $line, 3); + $offset = hex($offset); + $map{$symbol} = [ $offset, $type ]; + } while ($line = ); +} diff -urN ikd-ref/scripts/memleak/FAQ ikd/scripts/memleak/FAQ --- ikd-ref/scripts/memleak/FAQ Thu Jan 1 01:00:00 1970 +++ ikd/scripts/memleak/FAQ Tue Oct 3 20:08:45 2000 @@ -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 ikd-ref/scripts/memleak/Makefile ikd/scripts/memleak/Makefile --- ikd-ref/scripts/memleak/Makefile Thu Jan 1 01:00:00 1970 +++ ikd/scripts/memleak/Makefile Tue Oct 3 20:08:45 2000 @@ -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 ikd-ref/scripts/memleak/doalloc ikd/scripts/memleak/doalloc --- ikd-ref/scripts/memleak/doalloc Thu Jan 1 01:00:00 1970 +++ ikd/scripts/memleak/doalloc Tue Oct 3 20:08:45 2000 @@ -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 ikd-ref/scripts/memleak/docutvm ikd/scripts/memleak/docutvm --- ikd-ref/scripts/memleak/docutvm Thu Jan 1 01:00:00 1970 +++ ikd/scripts/memleak/docutvm Tue Oct 3 20:08:45 2000 @@ -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 ikd-ref/scripts/memleak/dodelta ikd/scripts/memleak/dodelta --- ikd-ref/scripts/memleak/dodelta Thu Jan 1 01:00:00 1970 +++ ikd/scripts/memleak/dodelta Tue Oct 3 20:08:45 2000 @@ -0,0 +1,9 @@ +#!/bin/bash + +# +# same as 'doalloc', but it lists delta allocations, not full number +# of allocations +# + +for N in `cut -f2 9*| sort| uniq`; do ( P=0;F=1;for M in `grep $N 9*| 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 ikd-ref/scripts/memleak/dofind ikd/scripts/memleak/dofind --- ikd-ref/scripts/memleak/dofind Thu Jan 1 01:00:00 1970 +++ ikd/scripts/memleak/dofind Tue Oct 3 20:08:45 2000 @@ -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 ikd-ref/scripts/memleak/dorun ikd/scripts/memleak/dorun --- ikd-ref/scripts/memleak/dorun Thu Jan 1 01:00:00 1970 +++ ikd/scripts/memleak/dorun Tue Oct 3 20:08:45 2000 @@ -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 ikd-ref/scripts/memleak/dosum ikd/scripts/memleak/dosum --- ikd-ref/scripts/memleak/dosum Thu Jan 1 01:00:00 1970 +++ ikd/scripts/memleak/dosum Tue Oct 3 20:08:45 2000 @@ -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 ikd-ref/scripts/memleak/dotimestamp ikd/scripts/memleak/dotimestamp --- ikd-ref/scripts/memleak/dotimestamp Thu Jan 1 01:00:00 1970 +++ ikd/scripts/memleak/dotimestamp Tue Oct 3 20:08:45 2000 @@ -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 ikd-ref/scripts/memleak/findaddr.c ikd/scripts/memleak/findaddr.c --- ikd-ref/scripts/memleak/findaddr.c Thu Jan 1 01:00:00 1970 +++ ikd/scripts/memleak/findaddr.c Tue Oct 3 20:08:45 2000 @@ -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 ikd-ref/scripts/memleak/memcheck.sh ikd/scripts/memleak/memcheck.sh --- ikd-ref/scripts/memleak/memcheck.sh Thu Jan 1 01:00:00 1970 +++ ikd/scripts/memleak/memcheck.sh Tue Oct 3 20:08:45 2000 @@ -0,0 +1,7 @@ +#!/bin/bash +TEMPFILE=`tempfile` +LOGFILE=/var/adm/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