diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/Documentation/Configure.help linux/Documentation/Configure.help --- /opt/kernel/linux-2.4.0-test1-ac21/Documentation/Configure.help Tue Jun 20 03:24:38 2000 +++ linux/Documentation/Configure.help Sat Jun 17 23:43:52 2000 @@ -511,6 +511,23 @@ say M here and read Documentation/modules.txt. The module will be called ide-cd.o. + +Packet writing on CD/DVD media (EXPERIMENTAL) +CONFIG_CDROM_PKTCDVD + If you have a CDROM drive that supports packet writing, say Y to + include preliminary support. It should work with any MMC/Mt Fuji + complain ATAPI or SCSI drive, which is just about any newer CD + writer. + + If you say Y here, you will be able to transparently write to CD-RW + and CD-R media. Writing is best used with the UDF file system, + so including that is recommended. + + If you want to compile the driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. The module will be + called packet.o. + Include IDE/ATAPI TAPE support CONFIG_BLK_DEV_IDETAPE If you have an IDE tape drive using the ATAPI protocol, say Y. @@ -13676,6 +13693,29 @@ send a BREAK and then within 5 seconds a command keypress. The keys are documented in Documentation/sysrq.txt. Don't say Y unless you really know what this hack does. + +Kernel Debugging support +CONFIG_KDB + This option provides a built-in kernel debugger. The built-in + kernel debugger contains commands which allow memory to be examined, + instructions to be disassembled and breakpoints to be set. For details, + see Documentation/kdb/kdb.mm and the manual pages kdb_bt, kdb_ss, etc. + Kdb can also be used via the serial port. Set up the system to + have a serial console (see Documentation/serial-console.txt). + The Control-A key sequence on the serial port will cause the + kernel debugger to be entered with input from the serial port and + output to the serial console. Selecting this option will + automatically set CONFIG_KALLSYMS. If unsure, say N. + +Load all symbols for debugging +CONFIG_KALLSYMS + Normally only exported symbols are available to modules. For + debugging you may want all symbols, not just the exported ones. If + you say Y here then extra data is added to the kernel and modules, + this data lists all the non-stack symbols in the kernel or module + and can be used by any debugger. You need modutils >= 2.3.11 to use + this option. See "man kallsyms" for the data format, it adds 10-20% + to the size of the kernel and the loaded modules. If unsure, say N. ISDN subsystem CONFIG_ISDN diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb.mm linux/Documentation/kdb/kdb.mm --- /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb.mm Thu Jan 1 01:00:00 1970 +++ linux/Documentation/kdb/kdb.mm Tue Jun 13 16:30:11 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_FRAME_POINTER\fP +option be enabled. \fBCONFIG_FRAME_POINTER\fP changes the compiler +flags so that the frame pointer register will be used as a frame +pointer rather than a general purpose register. +.P +After linux has been configured to include the kernel debugger, +make a new kernel with the new configuration file (a make clean +is recommended before making the kernel), and install the kernel +as normal. +.P +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 --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb_bp.man linux/Documentation/kdb/kdb_bp.man --- /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb_bp.man Thu Jan 1 01:00:00 1970 +++ linux/Documentation/kdb/kdb_bp.man Tue Jun 13 16:30:11 2000 @@ -0,0 +1,172 @@ +.TH BD 1 "19 May 2000" +.SH NAME +bp, bpa, bph, bpha, bd, bc, be, bl \- breakpoint commands +.SH SYNOPSIS +bp \fIaddress-expression\fP +.LP +bpa \fIaddress-expression\fP +.LP +bph \fIaddress-expression\fP [\f(CWDATAR|DATAW|IO\fP [\fIlength\fP]] +.LP +bpha \fIaddress-expression\fP [\f(CWDATAR|DATAW|IO\fP [\fIlength\fP]] +.LP +bd \fIbreakpoint-number\fP +.LP +bc \fIbreakpoint-number\fP +.LP +be \fIbreakpoint-number\fP +.LP +bl +.SH DESCRIPTION +.hy 0 +The +.B bp +family of commands are used to establish a breakpoint. +The \fIaddress-expression\fP may be a numeric value (decimal or +hexidecimal), a symbol name, a register name preceeded by a +percent symbol '%', or a simple expression consisting of a +symbol name, an addition or subtraction character and a numeric +value (decimal or hexidecimal). +.P +\fBbph\fP and \fBbpha\fP will force the use of a hardware register, provided +the processor architecture supports them. +.P +The \fIaddress-expression\fP may also consist of a single +asterisk '*' symbol which indicates that the command should +operate on all existing breakpoints (valid only for \fBbc\fP, +\fBbd\fP and \fBbe\fP). +.P +Four different types of +breakpoints may be set: + +.TP 8 +Instruction +Causes the kernel debugger to be invoked from the debug exception +path when an instruction is fetched from the specified address. This +is the default if no other type of breakpoint is requested or when +the \fBbp\fP command is used. + +.TP 8 +DATAR +Causes the kernel debugger to be entered when data of length +\fIlength\fP is read from or written to the specified address. +This type of breakpoint must use a processor debug register which +places an architecture dependent limit on the number of data and I/O +breakpoints that may be established. +The \fBbph\fP or \fBbpha\fP commands must be used. + +.TP 8 +DATAW +Enters the kernel debugger when data of length \fIlength\fP +is written to the specified address. \fIlength\fP defaults +to four bytes if it is not explicitly specified. +Note that the processor may have already overwritten the prior data at +the breakpoint location before the kernel debugger is invoked. +The prior data should be saved before establishing the breakpoint, if +required. +The \fBbph\fP or \fBbpha\fP commands must be used. + +.TP 8 +IO +Enters the kernel debugger when an \fBin\fP or \fBout\fP instruction +targets the specified I/O address. The \fBbph\fP or \fBbpha\fP +commands must be used. + +.P +The +.B bpha +command will establish a breakpoint on all processors in an +SMP system. This command is not available in an uniprocessor +kernel. +.P +The +.B bd +command will disable a breakpoint without removing it from the kernel +debugger's breakpoint table. +This can be used to keep breakpoints in the table without exceeding the +architecture limit on breakpoint registers. +.P +The +.B be +command will re-enable a disabled breakpoint. +.P +The +.B bc +command will clear a breakpoint from the breakpoint table. +.P +The +.B bl +command will list the existing set of breakpoints. +.SH LIMITATIONS +There is a compile time limit of sixteen entries in the +breakpoint table at any one time. +.P +There are architecture dependent limits on the number of hardware +breakpoints that can be set. +.IP ix86 8 +Four. +.PD 0 +.IP ia64 8 +? +.PD 1 +.SH ENVIRONMENT +The breakpoint subsystem does not currently use any environment +variables. +.SH SMP CONSIDERATIONS +Using +.B bc +is risky on SMP systems. +If you clear a breakpoint when another cpu has hit that breakpoint but +has not been processed then it may not be recognised as a kdb +breakpoint, usually resulting in incorrect program counters and kernel +panics. +It is safer to disable the breakpoint with +.BR bd , +then +.B go +to let any other processors that are waiting on the breakpoint to +clear. +After all processors are clear of the disabled breakpoint then it is +safe to clear it using +.BR bc . +.P +Breakpoints which use the processor breakpoint registers +are only established on the processor which is +currently active. If you wish breakpoints to be universal +use the +.B bpa +or +.B bpha +commands. +.SH EXAMPLES +.TP 8 +bp schedule +Sets an instruction breakpoint at the begining of the +function \fBschedule\fP. + +.TP 8 +bp schedule+0x12e +Sets an instruction breakpoint at the instruction located +at \fBschedule\fP+\fI0x12e\fP. + +.TP 8 +bph ttybuffer+0x24 dataw +Sets a data write breakpoint at the location referenced by +\fBttybuffer\fP+\fI0x24\fP for a length of four bytes. + +.TP 8 +bph 0xc0254010 datar 1 +Establishes a data reference breakpoint at address \fB0xc0254010\fP +for a length of one byte. + +.TP 8 +bp +List current breakpoint table. + +.TP 8 +bd 0 +Disable breakpoint #0. + +.TP 8 +bc * +Clear all breakpoints diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb_bt.man linux/Documentation/kdb/kdb_bt.man --- /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb_bt.man Thu Jan 1 01:00:00 1970 +++ linux/Documentation/kdb/kdb_bt.man Tue Jun 13 16:30:11 2000 @@ -0,0 +1,162 @@ +.TH BT 1 "17 May 2000" +.SH NAME +bt \- Stack Traceback command +.SH SYNOPSIS +bt [ ] +.LP +btp +.SH DESCRIPTION +.hy 0 +The +.B bt +command is used to print a stack traceback. It uses the +current registers (see \fBrd\fP command) to determine +the starting context and attempts to provide a complete +stack traceback for the active thread. If \fIstack-frame-address\fP +is supplied, it is assumed to point to the start of a valid +stack frame and the stack will be traced back from that +point (e.g. on i386 architecture, \fIstack-frame-address\fP +should be the stack address of a saved \fB%eip\fP value from a \fBcall\fP +instruction). +.P +A kernel configuration option \fBCONFIG_FRAME_POINTER\fP should +be enabled so that the compiler will utilize the frame pointer +register properly to maintain a stack which can be correctly +analyzed. +.P +The \fBbt\fP command will attempt to analyze the stack without +frame pointers if the \fBCONFIG_FRAME_POINTER\fP option is not +enabled, but the analysis is difficult and may not produce +accurate nor complete results. +.P +The \fBbtp\fP command will analyze the stack for the given +process identification (see the \fBps\fP command). +.P +For each function, the stack trace prints at least two lines. +The first line contains four or five fields\ :- +.IP * 3 +The pointer to the previous stack frame, blank if there is no valid +frame pointer. +.PD 0 +.IP * 3 +The current address within this frame. +.IP * 3 +The address converted to a function name (actually the first non-local +label which is <= the address). +.IP * 3 +The offset of the address within the function. +.IP * 3 +Any parameters to the function. +.PD 1 +.PP +On the next line there are five fields which are designed to make it +easier to match the trace against the kernel code\ :- +.IP * 3 +The module name that contains the address, "kernel" if it is in the +base kernel. +.PD 0 +.IP * 3 +The section name that contains the address. +.IP * 3 +The start address of the section. +.IP * 3 +The start address of the function. +.IP * 3 +The end address of the function (the first non-local label which is > +the address). +.PD 1 +.PP +If arguments are being converted to symbols, any argument which +converts to a kernel or module address is printed as\ :- +.IP * 3 +Argument address. +.PD 0 +.IP * 3 +The module name that contains the address, "kernel" if it is in the +base kernel. +.IP * 3 +The symbol name the argument maps to. +.IP * 3 +The offset of the argument from the symbol, suppressed if 0. +.PD 1 +.SH MATCHING TRACE TO KERNEL CODE +The command "objdump\ -S" will disassemble an object and, if the code +was compiled with debugging (gcc flag -g), objdump will interleave the +C source lines with the generated object. +.PP +A complete objdump of the kernel or a module is too big, normally you +only want specific functions. +By default objdump will only print the .text section but Linux uses +other section names for executable code. +When objdump prints relocatable objects (modules) it uses an offset of +0 which is awkward to relate to the stack trace. +The five fields which are printed for each function are designed to +make it easier to match the stack trace against the kernel code using +"objdump\ -S". +.PP +If the function is in the kernel then you need the section name, the +start and end address of the function. The command is +.PP +.nf + objdump -S -j \\ + --start-address= \\ + --stop-address= \\ + /usr/src/linux/vmlinux +.fi +.PP +If the function is in a module then you need the section name, the +start address of the section, the start and end address of the +function, the module name. The command is +.PP +.nf + objdump -S -j \\ + --adjust-vma= \\ + --start-address= \\ + --stop-address= \\ + /path/to/module/.o +.fi +.PP +All addresses to objdump must be preceded by '0x' if they are in hex, +objdump does not assume hex. +The stack trace values are printed with leading '0x' to make it easy to +run objdump. +.SH LIMITATIONS +If the kernel is compiled without frame pointers, stack tracebacks +may be incomplete. The \fBmds %esp\fP command may be useful in +attemping to determine the actual stack traceback manually. +.P +A stack trace can be misleading if any code in a function exit has been +executed, the stack is partially unwound at that stage. +.P +The \fBbt\fP command may print more arguments for a function +than that function accepts; this happens when the C compiler +doesn't immediately pop the arguments off the stack upon return +from a called function. When this is this case, these extra +stack words will be considered additional arguments by the \fBbt\fP +command. +.SH ENVIRONMENT +The \fBBTARGS\fP environment variable governs the maximum number +of arguments that are printed for any single function. +.PP +If the \fBBTSYMARG\fP environment variable is non-zero then any +arguments that fall within the kernel are converted to symbols. +.PP +If the \fBNOSECT\fP environment variable is non-zero then the +section information is suppressed. +.SH SMP CONSIDERATIONS +None. +.SH EXAMPLES +.nf +.na +.ft CW +Entering kdb (0xc3cb4000) due to Breakpoint @ 0xc011725d +Instruction(i) breakpoint #0 at 0xc011725c +qm_modules+0xd1: movl %ebp,%esp +kdb> bt + EBP EIP Function(args) +0xc3cb5f98 0xc011725d qm_modules+0xd1 (0x80721c0, 0x100, 0xbfff5000) + kernel .text 0xc0100000 0xc011718c 0xc0117264 +0xc3cb5fbc 0xc0117875 sys_query_module+0x1b1 (0x0, 0x1, 0x80721c0, 0x100, 0xbfff5000) + kernel .text 0xc0100000 0xc01176c4 0xc01178e8 + 0xc01095f8 system_call+0x34 + kernel .text 0xc0100000 0xc01095c4 0xc01095fc diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb_env.man linux/Documentation/kdb/kdb_env.man --- /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb_env.man Thu Jan 1 01:00:00 1970 +++ linux/Documentation/kdb/kdb_env.man Tue Jun 13 16:30:11 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 --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb_ll.man linux/Documentation/kdb/kdb_ll.man --- /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb_ll.man Thu Jan 1 01:00:00 1970 +++ linux/Documentation/kdb/kdb_ll.man Tue Jun 13 16:30:11 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 --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb_md.man linux/Documentation/kdb/kdb_md.man --- /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb_md.man Thu Jan 1 01:00:00 1970 +++ linux/Documentation/kdb/kdb_md.man Tue Jun 13 16:30:11 2000 @@ -0,0 +1,91 @@ +.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. +By default the section data is printed for kernel symbols. +.P +The +.B mm +command allows modification of memory. The word at the address +represented by \fIaddress-expression\fP is changed to +\fInew-contents\fP. \fInew-contents\fP is allowed to be an +\fIaddress-expression\fP. +.SH LIMITATIONS +None. +.SH ENVIRONMENT +.TP 8 +MDCOUNT +This environment variable (default=8) defines the number of lines +that will be displayed by each invocation of the \fBmd\fP command. + +.TP 8 +RADIX +This environment variable (default=16) defines the radix used to +print the memory contents. + +.TP 8 +BYTESPERWORD +This environment variable (default=4) selects the width of output +data when printing memory contents. Select the value two to get +16-bit word output, select the value one to get byte output. + +.TP 8 +LINES +This environment variable governs the number of lines of output +that will be presented before the kernel debugger built-in pager +pauses the output. This variable only affects the functioning +of the \fBmd\fP and \fBmds\fP if the \fBMDCOUNT\fP variable +is set to a value greater than the \fBLINES\fP variable. + +.TP 8 +If the \fBNOSECT\fP environment variable is non-zero then the +section information is suppressed. +.SH SMP CONSIDERATIONS +None. +.SH EXAMPLES +.TP 8 +md %edx +Display memory starting at the address contained in register \fB%edx\fP. + +.TP 8 +mds %esp +Display stack contents symbolically. This command is quite useful +in manual stack traceback. + +.TP 8 +mm 0xc0252110 0x25 +Change the memory location at 0xc0252110 to the value 0x25. + +.TP 8 +md chrdev_table 15 +Display 15 lines (at 16 bytes per line) starting at address +represented by the symbol \fIchrdev_table\fP. diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb_rd.man linux/Documentation/kdb/kdb_rd.man --- /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb_rd.man Thu Jan 1 01:00:00 1970 +++ linux/Documentation/kdb/kdb_rd.man Tue Jun 13 16:30:11 2000 @@ -0,0 +1,62 @@ +.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. +.P +The debug registers, \fBdr0\fP through \fBdr3\fP and both +\fBdr6\fP and \fBdr7\fP can also be modified with the \fBrm\fP +command. +.SH LIMITATIONS +Currently the \fBrm\fP command will not allow modification of the +control registers. +.P +Currently neither the \fBrd\fP command nor the \fBrm\fP command will +display or modify the model specific registers on the Pentium +and Pentium Pro families. +.SH ENVIRONMENT +None. +.SH SMP CONSIDERATIONS +None. +.SH EXAMPLES +.TP 8 +rd +Display general register set. + +.TP 8 +rm %eax 0 +Set the contents of \fB%eax\fP to zero. This will be the +value of %eax when kdb returns from the condition which +invoked it. + +.TP 8 +rm %%eax 0 +Set the value of the \fB%eax\fP register to zero. This will +be the value the user-mode application will see upon returning +from the kernel. + +.TP 8 +rm dr0 0xc1287220 +Set the value of the \fBdr0\fB register to \f(CW0xc1287220\fP. diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb_ss.man linux/Documentation/kdb/kdb_ss.man --- /opt/kernel/linux-2.4.0-test1-ac21/Documentation/kdb/kdb_ss.man Thu Jan 1 01:00:00 1970 +++ linux/Documentation/kdb/kdb_ss.man Tue Jun 13 16:30:11 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 --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/Makefile linux/Makefile --- /opt/kernel/linux-2.4.0-test1-ac21/Makefile Tue Jun 20 03:24:38 2000 +++ linux/Makefile Mon Jun 19 01:05:27 2000 @@ -35,6 +35,7 @@ OBJDUMP = $(CROSS_COMPILE)objdump MAKEFILES = $(TOPDIR)/.config GENKSYMS = /sbin/genksyms +KALLSYMS = /sbin/kallsyms MODFLAGS = -DMODULE PERL = perl @@ -82,7 +83,10 @@ CPPFLAGS := -D__KERNEL__ -I$(HPATH) -CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 +ifndef CONFIG_FRAME_POINTER +CFLAGS += -fomit-frame-pointer +endif AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS) # use '-fno-strict-aliasing', but only if the compiler can take it @@ -127,6 +131,11 @@ LIBS =$(TOPDIR)/lib/lib.a SUBDIRS =kernel drivers mm fs net ipc lib +ifeq ($(CONFIG_KDB),y) +CORE_FILES += kdb/kdb.o +SUBDIRS += kdb +endif + DRIVERS-n := DRIVERS-y := DRIVERS-m := @@ -190,16 +199,34 @@ boot: vmlinux @$(MAKE) -C arch/$(ARCH)/boot +LD_VMLINUX := $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \ + --start-group \ + $(CORE_FILES) \ + $(DRIVERS) \ + $(NETWORKS) \ + $(LIBS) \ + --end-group +ifeq ($(CONFIG_KALLSYMS),y) +LD_VMLINUX_KALLSYMS := .tmp_kallsyms3.o +else +LD_VMLINUX_KALLSYMS := +endif + vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs - $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \ - --start-group \ - $(CORE_FILES) \ - $(DRIVERS) \ - $(NETWORKS) \ - $(LIBS) \ - --end-group \ - -o vmlinux +ifeq ($(CONFIG_KALLSYMS),y) + @echo kallsyms pass 1 + $(LD_VMLINUX) -o .tmp_vmlinux1 + @$(KALLSYMS) .tmp_vmlinux1 > .tmp_kallsyms1.o + @echo kallsyms pass 2 + @$(LD_VMLINUX) .tmp_kallsyms1.o -o .tmp_vmlinux2 + @$(KALLSYMS) .tmp_vmlinux2 > .tmp_kallsyms2.o + @echo kallsyms pass 3 + @$(LD_VMLINUX) .tmp_kallsyms2.o -o .tmp_vmlinux3 + @$(KALLSYMS) .tmp_vmlinux3 > .tmp_kallsyms3.o +endif + $(LD_VMLINUX) $(LD_VMLINUX_KALLSYMS) -o vmlinux $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aU] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map + @rm -f .tmp_vmlinux* .tmp_kallsyms* symlinks: rm -f include/asm diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/arm/vmlinux-armo.lds.in linux/arch/arm/vmlinux-armo.lds.in --- /opt/kernel/linux-2.4.0-test1-ac21/arch/arm/vmlinux-armo.lds.in Mon Mar 13 04:39:39 2000 +++ linux/arch/arm/vmlinux-armo.lds.in Tue Jun 13 16:30:11 2000 @@ -55,6 +55,10 @@ *(__ksymtab) __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + *(__kallsyms) + __stop___kallsyms = .; + *(.got) /* Global offset table */ _etext = .; /* End of text section */ diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/arm/vmlinux-armv.lds.in linux/arch/arm/vmlinux-armv.lds.in --- /opt/kernel/linux-2.4.0-test1-ac21/arch/arm/vmlinux-armv.lds.in Mon Mar 13 04:39:39 2000 +++ linux/arch/arm/vmlinux-armv.lds.in Tue Jun 13 16:30:11 2000 @@ -50,6 +50,10 @@ *(__ksymtab) __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + *(__kallsyms) + __stop___kallsyms = .; + *(.got) /* Global offset table */ _etext = .; /* End of text section */ diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/Makefile linux/arch/i386/Makefile --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/Makefile Tue Jun 20 03:24:38 2000 +++ linux/arch/i386/Makefile Sat Jun 17 23:43:53 2000 @@ -83,6 +83,11 @@ CORE_FILES := arch/i386/kernel/kernel.o arch/i386/mm/mm.o $(CORE_FILES) LIBS := $(TOPDIR)/arch/i386/lib/lib.a $(LIBS) $(TOPDIR)/arch/i386/lib/lib.a +ifdef CONFIG_KDB +LIBS := $(LIBS) $(TOPDIR)/arch/i386/kdb/kdba.o +SUBDIRS := $(SUBDIRS) arch/i386/kdb +endif + ifdef CONFIG_MATH_EMULATION SUBDIRS += arch/i386/math-emu DRIVERS += arch/i386/math-emu/math.a @@ -93,6 +98,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 --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/config.in linux/arch/i386/config.in --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/config.in Tue Jun 20 03:24:38 2000 +++ linux/arch/i386/config.in Mon Jun 19 01:05:27 2000 @@ -341,4 +341,12 @@ #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +bool 'Built-in Kernel Debugger support' CONFIG_KDB +if [ "$CONFIG_KDB" = "y" ]; then + comment 'Load all symbols for debugging is required for KDB' + define_bool CONFIG_KALLSYMS y +else + bool 'Load all symbols for debugging' CONFIG_KALLSYMS +fi +bool 'Compile the kernel with frame pointers' CONFIG_FRAME_POINTER endmenu diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kdb/Makefile linux/arch/i386/kdb/Makefile --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kdb/Makefile Thu Jan 1 01:00:00 1970 +++ linux/arch/i386/kdb/Makefile Tue Jun 13 16:30:11 2000 @@ -0,0 +1,7 @@ +EXTRA_CFLAGS=-g +O_TARGET := kdba.o +O_OBJS = kdba_bt.o kdba_bp.o kdba_id.o kdba_io.o kdbasupport.o i386-dis.o + +override CFLAGS := $(CFLAGS:%-pg=% ) + +include $(TOPDIR)/Rules.make diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kdb/i386-dis.c linux/arch/i386/kdb/i386-dis.c --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kdb/i386-dis.c Thu Jan 1 01:00:00 1970 +++ linux/arch/i386/kdb/i386-dis.c Tue Jun 13 16:30:11 2000 @@ -0,0 +1,2311 @@ +/* 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. + */ + +#if defined(__KERNEL__) +#include +#include +#include +#include +#else +#include "dis-asm.h" +#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? */ + kdb_printf("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__) + kdb_printf("Bad machine type\n"); +#else + abort (); +#endif + return 0; +} + +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 --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kdb/kdba_bp.c linux/arch/i386/kdb/kdba_bp.c --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kdb/kdba_bp.c Thu Jan 1 01:00:00 1970 +++ linux/arch/i386/kdb/kdba_bp.c Tue Jun 13 16:30:11 2000 @@ -0,0 +1,730 @@ +/* + * Kernel Debugger Architecture Dependent Breakpoint Handling + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ + +#include +#include +#include +#include +#include + + +static char *kdba_rwtypes[] = { "Instruction(Register)", "Data Write", + "I/O", "Data Access"}; + +/* + * Table describing processor architecture hardware + * breakpoint registers. + */ + +kdbhard_bp_t kdb_hardbreaks[KDB_MAXHARDBPT]; + +/* + * kdba_db_trap + * + * Perform breakpoint processing upon entry to the + * processor debugger fault. Determine and print + * the active breakpoint. + * + * Parameters: + * ef Exception frame containing machine register state + * Outputs: + * None. + * Returns: + * 0 Standard instruction or data breakpoint encountered + * 1 Single Step fault ('ss' command) + * 2 Single Step fault, caller should continue ('ssb' command) + * 3 No existing kdb breakpoint matches this debug exception + * Locking: + * None. + * Remarks: + * Yup, there be goto's here. + * + * If multiple processors receive debug exceptions simultaneously, + * one may be waiting at the kdb fence in kdb() while the user + * issues a 'bc' command to clear the breakpoint the processor which + * is waiting has already encountered. If this is the case, the + * debug registers will no longer match any entry in the breakpoint + * table, and we'll return the value '3'. This can cause a panic + * in die_if_kernel(). It is safer to disable the breakpoint (bd), + * go until all processors are past the breakpoint then clear the + * breakpoint (bc). This code recognises a breakpoint even when + * disabled but not when it has been cleared. + * + * WARNING: This routine clears the debug state. It should be called + * once per debug and the result cached. + */ + +kdb_dbtrap_t +kdba_db_trap(kdb_eframe_t ef) +{ + kdb_machreg_t dr6; + kdb_machreg_t dr7; + int rw, reg; + int i; + kdb_dbtrap_t rv = KDB_DB_BPT; + kdb_bp_t *bp; + + dr6 = kdb_getdr6(); + dr7 = kdb_getdr7(); + + if (kdb_flags & KDB_DEBUG_BP) + kdb_printf("kdb: dr6 0x%lx dr7 0x%lx\n", dr6, dr7); + if (dr6 & DR6_BS) { + if (kdb_flags & KDB_FLAG_SSBPT) { + kdb_flags &= ~KDB_FLAG_SSBPT; + for(i=0,bp=kdb_breakpoints; + i < KDB_MAXBPT; + i++, bp++) { + if (bp->bp_enabled) { + if (kdb_flags & KDB_DEBUG_BP) + kdb_printf("bp 0x%p delayed %d\n", + bp, bp->bp_delayed); + } + if (bp->bp_delayed) { + bp->bp_delayed = 0; + kdba_installbp(ef, bp); + if (!(kdb_flags & KDB_FLAG_SS)) { + ef->eflags &= ~EF_TF; + } + break; + } + } + if (i == KDB_MAXBPT) { + kdb_printf("kdb: Unable to find delayed breakpoint\n"); + } + /* FALLTHROUGH */ + } + + if ((kdb_flags & KDB_FLAG_SS) == 0) + goto unknown; + + /* single step */ + rv = KDB_DB_SS; /* Indicate single step */ + if (kdb_flags & KDB_FLAG_SSB) { + unsigned char op1, op2 = 0; + + op1 = (unsigned char)kdba_getword(ef->eip, sizeof(op1)); + if (op1 == 0x0f) { + op2 = (unsigned char)kdba_getword(ef->eip+1, sizeof(op2)); + } + if (((op1&0xf0) == 0xe0) /* short disp jumps */ + || ((op1&0xf0) == 0x70) /* Misc. jumps */ + || (op1 == 0xc2) /* ret */ + || (op1 == 0x9a) /* call */ + || ((op1&0xf8) == 0xc8) /* enter, leave, iret, int, */ + || ((op1 == 0x0f) + && ((op2&0xf0)== 0x80))) { + /* + * End the ssb command here. + */ + kdb_flags &= ~(KDB_FLAG_SSB|KDB_FLAG_SS); + } else { + kdb_id1(ef->eip); + rv = KDB_DB_SSB; /* Indicate ssb - dismiss immediately */ + } + } else { + /* + * Print current insn + */ + kdb_printf("SS trap at "); + kdb_symbol_print(ef->eip, NULL, KDB_SP_DEFAULT|KDB_SP_NEWLINE); + kdb_id1(ef->eip); + kdb_flags &= ~KDB_FLAG_SS; + } + + if (rv != KDB_DB_SSB) + ef->eflags &= ~EF_TF; + } + + if (dr6 & DR6_B0) { + rw = DR7_RW0(dr7); + reg = 0; + goto handle; + } + + if (dr6 & DR6_B1) { + rw = DR7_RW1(dr7); + reg = 1; + goto handle; + } + + if (dr6 & DR6_B2) { + rw = DR7_RW2(dr7); + reg = 2; + goto handle; + } + + if (dr6 & DR6_B3) { + rw = DR7_RW3(dr7); + reg = 3; + goto handle; + } + + if (rv > 0) + goto handled; + + goto unknown; /* dismiss */ + +handle: + /* + * Set Resume Flag + */ + ef->eflags |= EF_RF; + + /* + * Determine which breakpoint was encountered. + */ + for(i=0, bp=kdb_breakpoints; ibp_free) + && (bp->bp_hard) + && (bp->bp_hard->bph_reg == reg)) { + /* + * Hit this breakpoint. + */ + kdb_printf("%s breakpoint #%d at 0x%lx\n", + kdba_rwtypes[rw], + i, bp->bp_addr); + + /* + * For an instruction breakpoint, disassemble + * the current instruction. + */ + if (rw == 0) { + kdb_id1(ef->eip); + } + + goto handled; + } + } + +unknown: + ef->eflags |= EF_RF; /* Supress further faults */ + rv = KDB_DB_NOBPT; /* Cause kdb() to return */ + +handled: + + /* + * Clear the pending exceptions. + */ + kdb_putdr6(0); + + return rv; +} + +/* + * kdba_bp_trap + * + * Perform breakpoint processing upon entry to the + * processor breakpoint instruction fault. Determine and print + * the active breakpoint. + * + * Parameters: + * ef Exception frame containing machine register state + * Outputs: + * None. + * Returns: + * 0 Standard instruction or data breakpoint encountered + * 1 Single Step fault ('ss' command) + * 2 Single Step fault, caller should continue ('ssb' command) + * 3 No existing kdb breakpoint matches this debug exception + * Locking: + * None. + * Remarks: + * + * If multiple processors receive debug exceptions simultaneously, + * one may be waiting at the kdb fence in kdb() while the user + * issues a 'bc' command to clear the breakpoint the processor which + * is waiting has already encountered. If this is the case, the + * debug registers will no longer match any entry in the breakpoint + * table, and we'll return the value '3'. This can cause a panic + * in die_if_kernel(). It is safer to disable the breakpoint (bd), + * 'go' until all processors are past the breakpoint then clear the + * breakpoint (bc). This code recognises a breakpoint even when + * disabled but not when it has been cleared. + * + * WARNING: This routine resets the eip. It should be called + * once per breakpoint and the result cached. + */ + +kdb_dbtrap_t +kdba_bp_trap(kdb_eframe_t ef) +{ + int i; + kdb_dbtrap_t rv; + kdb_bp_t *bp; + + /* + * Determine which breakpoint was encountered. + */ + if (kdb_flags & KDB_DEBUG_BP) + kdb_printf("kdba_bp_trap: eip=0x%lx (not adjusted) " + "eflags=0x%lx ef=0x%p esp=0x%lx\n", + ef->eip, ef->eflags, ef, ef->esp); + + rv = KDB_DB_NOBPT; /* Cause kdb() to return */ + + for(i=0, bp=kdb_breakpoints; ibp_free) + continue; + if (bp->bp_addr == (ef->eip - bp->bp_adjust)) { + /* Hit this breakpoint. */ + ef->eip -= bp->bp_adjust; + kdb_printf("Instruction(i) breakpoint #%d at 0x%lx (adjusted)\n", + i, ef->eip); + kdb_id1(ef->eip); + rv = KDB_DB_BPT; + bp->bp_delay = 1; + break; + } + } + + return rv; +} + +/* + * kdba_handle_bp + * + * Handle an instruction-breakpoint trap. Called when re-installing + * an enabled breakpoint which has has the bp_delay bit set. + * + * Parameters: + * Returns: + * Locking: + * Remarks: + * + * Ok, we really need to: + * 1) Restore the original instruction byte + * 2) Single Step + * 3) Restore breakpoint instruction + * 4) Continue. + * + * + */ + +static void +kdba_handle_bp(kdb_eframe_t ef, kdb_bp_t *bp) +{ + if (kdb_flags & KDB_DEBUG_BP) + kdb_printf("ef->eip = 0x%lx\n", ef->eip); + + /* + * Setup single step + */ + kdba_setsinglestep(ef); + + /* + * Mark single-step event as a kdb internal event + */ + kdb_flags |= KDB_FLAG_SSBPT; + + /* + * Reset delay attribute + */ + bp->bp_delay = 0; + bp->bp_delayed = 1; +} + + +/* + * kdba_bptype + * + * Return a string describing type of breakpoint. + * + * Parameters: + * bph Pointer to hardware breakpoint description + * Outputs: + * None. + * Returns: + * Character string. + * Locking: + * None. + * Remarks: + */ + +char * +kdba_bptype(kdbhard_bp_t *bph) +{ + char *mode; + + mode = kdba_rwtypes[bph->bph_mode]; + + return mode; +} + +/* + * kdba_printbpreg + * + * Print register name assigned to breakpoint + * + * Parameters: + * bph Pointer hardware breakpoint structure + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdba_printbpreg(kdbhard_bp_t *bph) +{ + kdb_printf(" in dr%ld", bph->bph_reg); +} + +/* + * kdba_printbp + * + * Print string describing hardware breakpoint. + * + * Parameters: + * bph Pointer to hardware breakpoint description + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdba_printbp(kdb_bp_t *bp) +{ + kdb_printf("\n is enabled"); + if (bp->bp_hardtype) { + kdba_printbpreg(bp->bp_hard); + if (bp->bp_hard->bph_mode != 0) { + kdb_printf(" for %d bytes", + bp->bp_hard->bph_length+1); + } + } +} + +/* + * kdba_parsebp + * + * Parse architecture dependent portion of the + * breakpoint command. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + * for Ia32 architure, data access, data write and + * I/O breakpoints are supported in addition to instruction + * breakpoints. + * + * {datar|dataw|io|inst} [length] + */ + +int +kdba_parsebp(int argc, const char **argv, int *nextargp, kdb_bp_t *bp) +{ + int nextarg = *nextargp; + int diag; + kdbhard_bp_t *bph = &bp->bp_template; + + bph->bph_mode = 0; /* Default to instruction breakpoint */ + bph->bph_length = 0; /* Length must be zero for insn bp */ + if ((argc + 1) != nextarg) { + if (strnicmp(argv[nextarg], "datar", sizeof("datar")) == 0) { + bph->bph_mode = 3; + } else if (strnicmp(argv[nextarg], "dataw", sizeof("dataw")) == 0) { + bph->bph_mode = 1; + } else if (strnicmp(argv[nextarg], "io", sizeof("io")) == 0) { + bph->bph_mode = 2; + } else if (strnicmp(argv[nextarg], "inst", sizeof("inst")) == 0) { + bph->bph_mode = 0; + } else { + return KDB_ARGCOUNT; + } + + bph->bph_length = 3; /* Default to 4 byte */ + + nextarg++; + + if ((argc + 1) != nextarg) { + unsigned long len; + + diag = kdbgetularg((char *)argv[nextarg], + &len); + if (diag) + return diag; + + + if ((len > 4) || (len == 3)) + return KDB_BADLENGTH; + + bph->bph_length = len; + bph->bph_length--; /* Normalize for debug register */ + nextarg++; + } + + if ((argc + 1) != nextarg) + return KDB_ARGCOUNT; + + /* + * Indicate to architecture independent level that + * a hardware register assignment is required to enable + * this breakpoint. + */ + + bph->bph_free = 0; + } else { + if (kdb_flags & KDB_DEBUG_BP) + kdb_printf("kdba_bp: no args, forcehw is %d\n", bp->bp_forcehw); + if (bp->bp_forcehw) { + /* + * We are forced to use a hardware register for this + * breakpoint because either the bph or bpha + * commands were used to establish this breakpoint. + */ + bph->bph_free = 0; + } else { + /* + * Indicate to architecture dependent level that + * the instruction replacement breakpoint technique + * should be used for this breakpoint. + */ + bph->bph_free = 1; + bp->bp_adjust = 1; /* software, int 3 is one byte */ + } + } + + *nextargp = nextarg; + return 0; +} + +/* + * kdba_allocbp + * + * Associate a hardware register with a breakpoint. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * A pointer to the allocated register kdbhard_bp_t structure for + * success, Null and a non-zero diagnostic for failure. + * Locking: + * None. + * Remarks: + */ + +kdbhard_bp_t * +kdba_allocbp(kdbhard_bp_t *bph, int *diagp) +{ + int i; + kdbhard_bp_t *newbph; + + for(i=0,newbph=kdb_hardbreaks; i < KDB_MAXHARDBPT; i++, newbph++) { + if (newbph->bph_free) { + break; + } + } + + if (i == KDB_MAXHARDBPT) { + *diagp = KDB_TOOMANYDBREGS; + return NULL; + } + + *diagp = 0; + + /* + * Copy data from template. Can't just copy the entire template + * here because the register number in kdb_hardbreaks must be + * preserved. + */ + newbph->bph_data = bph->bph_data; + newbph->bph_write = bph->bph_write; + newbph->bph_mode = bph->bph_mode; + newbph->bph_length = bph->bph_length; + + /* + * Mark entry allocated. + */ + newbph->bph_free = 0; + + return newbph; +} + +/* + * kdba_freebp + * + * Deallocate a hardware breakpoint + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + */ + +void +kdba_freebp(kdbhard_bp_t *bph) +{ + bph->bph_free = 1; +} + +/* + * kdba_initbp + * + * Initialize the breakpoint table for the hardware breakpoint + * register. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + * + * There is one entry per register. On the ia32 architecture + * all the registers are interchangeable, so no special allocation + * criteria are required. + */ + +void +kdba_initbp(void) +{ + int i; + kdbhard_bp_t *bph; + + /* + * Clear the hardware breakpoint table + */ + + memset(kdb_hardbreaks, '\0', sizeof(kdb_hardbreaks)); + + for(i=0,bph=kdb_hardbreaks; ibph_reg = i; + bph->bph_free = 1; + } +} + +/* + * kdba_installbp + * + * Install a breakpoint + * + * Parameters: + * ef Exception frame + * bp Breakpoint structure for the breakpoint to be installed + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * For hardware breakpoints, a debug register is allocated + * and assigned to the breakpoint. If no debug register is + * available, a warning message is printed and the breakpoint + * is disabled. + * + * For instruction replacement breakpoints, we must single-step + * over the replaced instruction at this point so we can re-install + * the breakpoint instruction after the single-step. + */ + +void +kdba_installbp(kdb_eframe_t ef, kdb_bp_t *bp) +{ + /* + * Install the breakpoint, if it is not already installed. + */ + + if (!bp->bp_installed) { + if (bp->bp_hardtype) { + kdba_installdbreg(bp); + if (kdb_flags & KDB_DEBUG_BP) + kdb_printf("kdb: Installing hardware reg %ld at 0x%lx\n", + bp->bp_hard->bph_reg, bp->bp_addr); + } else if (bp->bp_delay) { + kdba_handle_bp(ef, bp); + } else { + bp->bp_inst = kdba_getword(bp->bp_addr, 1); + kdba_putword(bp->bp_addr, 1, IA32_BREAKPOINT_INSTRUCTION); + bp->bp_instvalid = 1; + if (kdb_flags & KDB_DEBUG_BP) + kdb_printf("kdb: Installing 0x%x at 0x%lx\n", + IA32_BREAKPOINT_INSTRUCTION, bp->bp_addr); + } + bp->bp_installed = 1; + } +} + +/* + * kdba_removebp + * + * Make a breakpoint ineffective. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdba_removebp(kdb_bp_t *bp) +{ + /* + * For hardware breakpoints, remove it from the active register, + * for software breakpoints, restore the instruction stream. + */ + if (bp->bp_installed) { + if (bp->bp_hardtype) { + if (kdb_flags & KDB_DEBUG_BP) + kdb_printf("kdb: removing hardware reg %ld at 0x%lx\n", + bp->bp_hard->bph_reg, bp->bp_addr); + kdba_removedbreg(bp); + } else if (bp->bp_instvalid) { + if (kdb_flags & KDB_DEBUG_BP) + kdb_printf("kdb: restoring instruction 0x%x at 0x%lx\n", + bp->bp_inst, bp->bp_addr); + kdba_putword(bp->bp_addr, 1, bp->bp_inst); + bp->bp_instvalid = 0; + } + bp->bp_installed = 0; + } +} diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kdb/kdba_bt.c linux/arch/i386/kdb/kdba_bt.c --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kdb/kdba_bt.c Thu Jan 1 01:00:00 1970 +++ linux/arch/i386/kdb/kdba_bt.c Tue Jun 13 16:30:11 2000 @@ -0,0 +1,323 @@ +/* + * Minimalist Kernel Debugger - Architecture Dependent Stack Traceback + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + * Masahiro Adegawa 1999/12/01 + * 'sr' command, active flag in 'ps' + * Scott Lurndal 1999/12/12 + * Significantly restructure for linux2.3 + * Keith Owens 2000/05/23 + * KDB v1.2 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * bt_print_one + * + * Print one back trace entry. + * + * Inputs: + * ebp Previous frame pointer, 0 if not valid. + * eip Current program counter. + * symtab Information about symbol that eip falls within. + * ar Activation record for this frame. + * argcount Maximum number of arguments to print. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +static void +bt_print_one(kdb_machreg_t eip, kdb_machreg_t ebp, const kdb_ar_t *ar, + const kdb_symtab_t *symtab, int argcount) +{ + int btsymarg = 0; + int nosect = 0; + + kdbgetintenv("BTSYMARG", &btsymarg); + kdbgetintenv("NOSECT", &nosect); + + if (ebp) + kdb_printf("0x%08lx", ebp); + else + kdb_printf(" "); + kdb_symbol_print(eip, symtab, KDB_SP_SPACEB|KDB_SP_VALUE); + if (argcount && ar->args) { + int i, argc = ar->args / 4; + + kdb_printf(" ("); + if (argc > argcount) + argc = argcount; + + for(i=1; i<=argc; i++){ + kdb_machreg_t argp = ar->arg0 - ar->args + 4*i; + + if (i != 1) + kdb_printf(", "); + kdb_printf("0x%lx", + kdba_getword(argp, sizeof(kdb_machreg_t))); + } + kdb_printf(")"); + } + if (symtab->sym_name) { + if (!nosect) { + kdb_printf("\n"); + kdb_printf(" %s %s 0x%lx 0x%lx 0x%lx", + symtab->mod_name, + symtab->sec_name, + symtab->sec_start, + symtab->sym_start, + symtab->sym_end); + } + } + kdb_printf("\n"); + if (argcount && ar->args && btsymarg) { + int i, argc = ar->args / 4; + kdb_symtab_t arg_symtab; + kdb_machreg_t arg; + for(i=1; i<=argc; i++){ + kdb_machreg_t argp = ar->arg0 - ar->args + 4*i; + arg = kdba_getword(argp, sizeof(kdb_machreg_t)); + if (kdbnearsym(arg, &arg_symtab)) { + kdb_printf(" "); + kdb_symbol_print(arg, &arg_symtab, KDB_SP_DEFAULT|KDB_SP_NEWLINE); + } + } + } +} + +/* + * kdba_bt_stack + * + * This function implements the 'bt' command. Print a stack + * traceback. + * + * bt [] (addr-exp is for alternate stacks) + * btp (Kernel stack for ) + * + * address expression refers to a return address on the stack. It + * may be preceeded by a frame pointer. + * + * Inputs: + * regs registers at time kdb was entered. + * addr Pointer to Address provided to 'bt' command, if any. + * argcount + * p Pointer to task for 'btp' command. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * mds comes in handy when examining the stack to do a manual + * traceback. + */ + +int +kdba_bt_stack(struct pt_regs *regs, kdb_machreg_t *addr, int argcount, + struct task_struct *p) +{ + kdb_ar_t ar; + kdb_machreg_t eip, esp, ebp, ss; + kdb_symtab_t symtab; + + /* + * The caller may have supplied an address at which the + * stack traceback operation should begin. This address + * is assumed by this code to point to a return-address + * on the stack to be traced back. The activation record + * which contains the return address must be complete + * enough that the prior activation record address (frame pointer) + * has been saved at the location immediately below the + * return-address. + * + * The end result of this will make it appear as if a function + * entitled '' was called from a the function which + * contains return-address. + */ + if (addr) { + eip = 0; + ebp = 0; + esp = *addr; + } else { + eip = regs->eip; + ebp = regs->ebp; + kdba_getregcontents("esp", regs, &esp); + } + ss = esp & -8192; + + kdb_printf(" EBP EIP Function(args)\n"); + + /* + * Run through the activation records and print them. + */ + + while (1) { + kdbnearsym(eip, &symtab); + if (!kdb_get_next_ar(esp, symtab.sym_start, eip, ebp, ss, + &ar, &symtab)) { + break; + } + + if (strcmp("stext_lock", symtab.sym_name) == 0) { + /* + * Instructions in the stext_lock area are generated by + * the out of line code in lock handling, see + * include/asm-i386 semaphore.h and rwlock.h. There can + * be multiple instructions which eventually end with a + * jump back to the mainline code. Use the disassmebler + * to silently step through the code until we find the + * jump, resolve its its destination and translate to a + * symbol. Replace 'stext_lock' with resolved symbol. + */ + unsigned char inst; + kdb_machreg_t offset = 0, realeip = eip; + int length, offsize = 0; + kdb_symtab_t lock_symtab; + /* Dummy out the disassembler print function */ + fprintf_ftype save_fprintf_func = kdb_di.fprintf_func; + + kdb_di.fprintf_func = &kdb_dis_fprintf_dummy; + while((length = kdba_id_printinsn(realeip, &kdb_di)) > 0) { + inst = kdba_getword(realeip, 1); + offsize = 0; + switch (inst) { + case 0xeb: /* jmp with 1 byte offset */ + offsize = 1; + offset = kdba_getword(realeip+1, offsize); + break; + case 0xe9: /* jmp with 4 byte offset */ + offsize = 4; + offset = kdba_getword(realeip+1, offsize); + break; + default: + realeip += length; /* next instruction */ + break; + } + if (offsize) + break; + } + kdb_di.fprintf_func = save_fprintf_func; + + if (offsize) { + realeip += 1 + offsize + offset; + if (kdbnearsym(realeip, &lock_symtab)) { + /* Print the stext entry without args */ + bt_print_one(eip, 0, &ar, &symtab, 0); + /* Point to mainline code */ + eip = realeip; + continue; + } + } + } + + if (strcmp("ret_from_intr", symtab.sym_name) == 0) { + /* + * Non-standard frame. ret_from_intr is preceded by + * 9 registers (ebx, ecx, edx, esi, edi, ebp, eax, ds, + * cs), original eax and the return address for a total + * of 11 words. + */ + ar.start = ar.end + 11*4; + /* Print the ret_from_intr entry without args */ + bt_print_one(eip, 0, &ar, &symtab, 0); + kdb_printf("Interrupt registers:\n"); + kdba_dumpregs((struct pt_regs *)(ar.end), NULL, NULL); + /* Step the frame to the interrupted code */ + eip = kdba_getword(ar.start-4, 4); + ebp = 0; + esp = ar.start; + continue; + } + + if (strcmp("error_code", symtab.sym_name) == 0) { + /* + * Non-standard frame. error_code is preceded + * by two parameters (-> registers, error code), + * 9 registers (ebx, ecx, edx, esi, edi, ebp, eax, ds, + * cs), original eax and the return address for a total + * of 13 words. + */ + ar.start = ar.end + 13*4; + /* Print the error_code entry without args */ + bt_print_one(eip, 0, &ar, &symtab, 0); + kdb_printf("Interrupt registers:\n"); + kdba_dumpregs((struct pt_regs *)(ar.end+8), NULL, NULL); + /* Step the frame to the interrupted code */ + eip = kdba_getword(ar.start-4, 4); + ebp = 0; + esp = ar.start; + continue; + } + + bt_print_one(eip, ebp, &ar, &symtab, argcount); + + eip = ar.ret; + ebp = ar.oldfp; + esp = ar.start; + } + + return 0; +} + +int +kdba_bt_process(struct task_struct *p, int argcount) +{ + struct pt_regs taskregs; + + + taskregs.eip = p->thread.eip; + taskregs.esp = p->thread.esp; + taskregs.ebp = 0; + /* + Not 2.3 + taskregs.ebp = p->thread.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 = + *(kdb_machreg_t *)(taskregs.esp); + } + + taskregs.eax = 0; + + return kdba_bt_stack(&taskregs, NULL, argcount, p); + +} diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kdb/kdba_id.c linux/arch/i386/kdb/kdba_id.c --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kdb/kdba_id.c Thu Jan 1 01:00:00 1970 +++ linux/arch/i386/kdb/kdba_id.c Tue Jun 13 16:30:11 2000 @@ -0,0 +1,279 @@ +/* + * Minimalist Kernel Debugger - Architecture Dependent Instruction Disassembly + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + * Masahiro Adegawa 1999/12/01 + * 'sr' command, active flag in 'ps' + * Scott Lurndal 1999/12/12 + * Significantly restructure for linux2.3 + * Keith Owens 2000/05/23 + * KDB v1.2 + * + */ + +#include +#include +#include +#include +#include +#include + +/* + * kdba_dis_getsym + * + * Get a symbol for the disassembler. + * + * Parameters: + * addr Address for which to get symbol + * dip Pointer to disassemble_info + * Returns: + * 0 + * Locking: + * Remarks: + * Not used for kdb. + */ + +/* ARGSUSED */ +static int +kdba_dis_getsym(bfd_vma addr, disassemble_info *dip) +{ + + return 0; +} + +/* + * kdba_printaddress + * + * Print (symbolically) an address. + * + * Parameters: + * addr Address for which to get symbol + * dip Pointer to disassemble_info + * flag True if a ":" sequence should follow the address + * Returns: + * 0 + * Locking: + * Remarks: + * + */ + +/* ARGSUSED */ +void +kdba_printaddress(bfd_vma addr, disassemble_info *dip, int flag) +{ + kdb_symtab_t symtab; + + /* + * Print a symbol name or address as necessary. + */ + kdbnearsym(addr, &symtab); + if (symtab.sym_name) { + /* Do not use kdb_symbol_print here, it always does + * kdb_printf but we want dip->fprintf_func. + */ + dip->fprintf_func(dip->stream, + "0x%0*lx %s", + 2*sizeof(addr), addr, symtab.sym_name); + if (addr != symtab.sym_start) + dip->fprintf_func(dip->stream, "+0x%x", addr - symtab.sym_start); + + } else { + dip->fprintf_func(dip->stream, "0x%x", addr); + } + + if (flag) + dip->fprintf_func(dip->stream, ": "); +} + +/* + * kdba_dis_printaddr + * + * Print (symbolically) an address. Called by GNU disassembly + * code via disassemble_info structure. + * + * Parameters: + * addr Address for which to get symbol + * dip Pointer to disassemble_info + * Returns: + * 0 + * Locking: + * Remarks: + * This function will never append ":" to the printed + * symbolic address. + */ + +static void +kdba_dis_printaddr(bfd_vma addr, disassemble_info *dip) +{ + kdba_printaddress(addr, dip, 0); +} + +/* + * kdba_dis_getmem + * + * Fetch 'length' bytes from 'addr' into 'buf'. + * + * Parameters: + * addr Address for which to get symbol + * buf Address of buffer to fill with bytes from 'addr' + * length Number of bytes to fetch + * dip Pointer to disassemble_info + * Returns: + * 0 + * Locking: + * Remarks: + * + */ + +/* ARGSUSED */ +static int +kdba_dis_getmem(bfd_vma addr, bfd_byte *buf, int length, disassemble_info *dip) +{ + bfd_byte *bp = buf; + int i; + + /* + * Fill the provided buffer with bytes from + * memory, starting at address 'addr' for 'length bytes. + * + */ + + for(i=0; imach = bfd_mach_i386_i386; + } else if (strcmp(mode, "8086") == 0) { + dip->mach = bfd_mach_i386_i8086; + } else { + return KDB_BADMODE; + } + } + + return 0; +} + +/* + * kdba_id_printinsn + * + * Format and print a single instruction at 'pc'. Return the + * length of the instruction. + * + * Parameters: + * pc Program Counter Value. + * dip Disassemble_info structure pointer + * Returns: + * Length of instruction, -1 for error. + * Locking: + * None. + * Remarks: + * Depends on 'IDMODE' environment variable. + */ + +int +kdba_id_printinsn(kdb_machreg_t pc, disassemble_info *dip) +{ + return print_insn_i386(pc, dip); +} + +/* + * kdba_id_init + * + * Initialize the architecture dependent elements of + * the disassembly information structure + * for the GNU disassembler. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic if failure. + * Locking: + * None. + * Remarks: + */ + +void +kdba_id_init(disassemble_info *dip) +{ + dip->fprintf_func = kdba_dis_fprintf; + dip->read_memory_func = kdba_dis_getmem; + dip->print_address_func = kdba_dis_printaddr; + dip->symbol_at_address_func = kdba_dis_getsym; + + dip->flavour = bfd_target_elf_flavour; + dip->arch = bfd_arch_i386; + dip->mach = bfd_mach_i386_i386; + dip->endian = BFD_ENDIAN_LITTLE; + + dip->display_endian = BFD_ENDIAN_LITTLE; +} diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kdb/kdba_io.c linux/arch/i386/kdb/kdba_io.c --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kdb/kdba_io.c Thu Jan 1 01:00:00 1970 +++ linux/arch/i386/kdb/kdba_io.c Tue Jun 13 16:30:11 2000 @@ -0,0 +1,314 @@ +/* + * Kernel Debugger Console I/O handler + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Chuck Fleckenstein 1999/07/20 + * Move kdb_info struct declaration to this file + * for cases where serial support is not compiled into + * the kernel. + * + * Masahiro Adegawa 1999/07/20 + * Handle some peculiarities of japanese 86/106 + * keyboards. + * + * marc@mucom.co.il 1999/07/20 + * Catch buffer overflow for serial input. + * + * Scott Foehner + * Port to ia64 + * + * Scott Lurndal 2000/01/03 + * Restructure for v1.0 + * + * Keith Owens 2000/05/23 + * KDB v1.2 + */ + +#include +#include +#include +#include +#include + +#include +#include + +int kdb_port = 0; + +/* + * This module contains code to read characters from the keyboard or a serial + * port. + * + * It is used by the kernel debugger, and is polled, not interrupt driven. + * + */ + +/* + * send: Send a byte to the keyboard controller. Used primarily to + * alter LED settings. + */ + +static void +kdb_kbdsend(unsigned char byte) +{ + while (inb(KBD_STATUS_REG) & KBD_STAT_IBF) + ; + outb(KBD_DATA_REG, byte); +} + +static void +kdb_kbdsetled(int leds) +{ + kdb_kbdsend(KBD_CMD_SET_LEDS); + kdb_kbdsend((unsigned char)leds); +} + +void +kdb_resetkeyboard(void) +{ +#if 0 + kdb_kbdsend(KBD_CMD_ENABLE); +#endif +} + +static char * +kdb_getscancode(char *buffer, size_t bufsize) +{ + char *cp = buffer; + int scancode, scanstatus; + static int shift_lock = 0; /* CAPS LOCK state (0-off, 1-on) */ + static int shift_key = 0; /* Shift next keypress */ + static int ctrl_key = 0; + static int leds = 2; /* Num lock */ + u_short keychar; + extern u_short plain_map[], shift_map[], ctrl_map[]; + + bufsize -= 2; /* Reserve space for newline and null byte */ + + /* + * If we came in via a serial console, we allow that to + * be the input window for kdb. + */ + if (kdb_port != 0) { + char ch; + int status; +#define serial_inp(info, offset) inb((info) + (offset)) +#define serial_out(info, offset, v) outb((v), (info) + (offset)) + + while(1) { + while ((status = serial_inp(kdb_port, UART_LSR)) + & UART_LSR_DR) { + ch = serial_inp(kdb_port, UART_RX); + if (ch == 8) { /* BS */ + if (cp > buffer) { + --cp, bufsize++; + kdb_printf("%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 + */ + kdb_printf("%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; + kdb_printf("Unknown state/scancode (%d)\n", scancode); + } + + if ((scancode & 0x7f) == 0x1c) { + /* + * enter key. All done. Absorb the release scancode. + */ + while ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0) + ; + + /* + * Fetch the scancode + */ + scancode = inb(KBD_DATA_REG); + scanstatus = inb(KBD_STATUS_REG); + + while (scanstatus & KBD_STAT_MOUSE_OBF) { + scancode = inb(KBD_DATA_REG); + scanstatus = inb(KBD_STATUS_REG); + } + + if (scancode != 0x9c) { + /* + * Wasn't an enter-release, why not? + */ + kdb_printf("kdb: expected enter got 0x%x status 0x%x\n", + scancode, scanstatus); + } + + kdb_printf("\n"); + break; + } + + /* + * echo the character. + */ + kdb_printf("%c", keychar&0xff); + + if (bufsize) { + --bufsize; + *cp++ = keychar&0xff; + } else { + kdb_printf("buffer overflow\n"); + break; + } + + } + + *cp++ = '\n'; /* White space for parser */ + *cp++ = '\0'; /* String termination */ + +#if defined(NOTNOW) + cp = buffer; + while (*cp) { + kdb_printf("char 0x%x\n", *cp++); + } +#endif + + return buffer; +} + +char * +kdba_read(char *buffer, size_t bufsize) +{ + return kdb_getscancode(buffer, bufsize); +} diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kdb/kdbasupport.c linux/arch/i386/kdb/kdbasupport.c --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kdb/kdbasupport.c Thu Jan 1 01:00:00 1970 +++ linux/arch/i386/kdb/kdbasupport.c Tue Jun 13 16:30:11 2000 @@ -0,0 +1,1508 @@ +/* + * Kernel Debugger Architecture Independent Support Functions + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +char *kdb_diemsg = NULL; +#if defined(CONFIG_SMP) +unsigned long smp_kdb_wait; +#endif + + +/* + * kdba_find_return + * + * Given a starting point on the stack and symtab data for the + * current function, scan up the stack looking for a return + * address for this function. + * Inputs: + * sp Starting stack pointer for scan + * ss Start of stack for current process + * symtab kallsyms symbol data for the function + * Outputs: + * None. + * Returns: + * Position on stack of return address, 0 if not found. + * Locking: + * None. + * Remarks: + * This is sensitive to the calling sequence generated by gcc. + */ + +static kdb_machreg_t +kdba_find_return(kdb_machreg_t sp, kdb_machreg_t ss, const kdb_symtab_t *symtab) +{ + kdb_machreg_t ret; + kdb_symtab_t caller_symtab; + + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" kdba_find_return: start\n"); + } + + if ((sp & -8192) != ss) { + kdb_printf(" sp is in wrong stack 0x%lx 0x%lx 0x%lx\n", sp, ss, sp & -8192); + return(0); + } + + if ((sp & (8192 - 1)) < sizeof(struct task_struct)) { + kdb_printf(" sp is inside task_struct\n"); + return(0); + } + + for (;ret = 0, sp & (8192-1);sp += 4) { + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" sp=0x%lx", sp); + } + ret = kdba_getword(sp, 4); + kdbnearsym(ret, &caller_symtab); + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" ret="); + kdb_symbol_print(ret, &caller_symtab, KDB_SP_DEFAULT|KDB_SP_SYMSIZE); + } + if (!caller_symtab.sym_name) { + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf("\n"); + } + continue; /* not a valid kernel address */ + } + if (kdba_getword(ret-5, 1) == 0xe8) { + /* call disp32 */ + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" call disp32"); + } + if (ret + kdba_getword(ret-4, 4) == symtab->sym_start) { + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" matched\n"); + } + break; /* call to this function */ + } + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" failed"); + } + } else if (kdba_getword(ret-7, 1) == 0xff && + kdba_getword(ret-6, 1) == 0x14 && + kdba_getword(ret-5, 1) == 0x85) { + /* call *0xnnnn(,%eax,4), used by syscall. + * Cannot calculate address, assume it is valid + * if the current function name starts with + * 'sys_' or 'old_'. + */ + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" call *0xnnnn(,%%eax,4)"); + } + if (strncmp(symtab->sym_name, "sys_", 4) == 0 || + strncmp(symtab->sym_name, "old_", 4) == 0) { + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" matched\n"); + } + break; /* probably call to this function */ + } + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" failed"); + } + } else if (kdba_getword(ret-2, 1) == 0xff && + (kdba_getword(ret-1, 1) & 0xf8) == 0xd0) { + /* call *%reg. Cannot validate, have to assume + * it is valid. + */ + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" call *%%reg, assume valid\n"); + } + break; /* hope it is a call to this function */ + } else if (kdba_getword(ret-5, 1) == 0xe9) { + /* jmp disp32. I have been told that gcc may + * do function tail optimization and replace + * call with jmp. + */ + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" jmp disp32\n"); + } + if (ret + kdba_getword(ret-4, 4) == symtab->sym_start) { + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" matched\n"); + } + break; /* jmp to this function */ + } + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" failed"); + } + } else if (kdba_getword(ret-2, 1) == 0xeb) { + /* jmp disp8 */ + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" jmp disp8\n"); + } + if (ret + kdba_getword(ret-1, 1) == symtab->sym_start) { + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" matched\n"); + } + break; /* jmp to this function */ + } + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" failed"); + } + } else if (strcmp(caller_symtab.sym_name, "ret_from_intr") == 0 + && ret == caller_symtab.sym_start) { + /* ret_from_intr is pushed on stack for interrupts */ + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" ret_from_intr matched\n"); + } + break; /* special case, hand crafted frame */ + } + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf("\n"); + } + } + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" end ret=0x%lx sp=0x%lx\n", ret, sp); + } + if (ret) + return(sp); + return(0); +} + +/* + * kdba_prologue + * + * This function analyzes a gcc-generated function prototype + * with or without frame pointers to determine the amount of + * automatic storage and register save storage is used on the + * stack of the target function. It only counts instructions + * that have been executed up to but excluding the current eip. + * Inputs: + * code Start address of function code to analyze + * pc Current program counter within function + * sp Current stack pointer for function + * fp Current frame pointer for function, may not be valid + * ss Start of stack for current process. + * caller 1 if looking for data on the caller frame, 0 for callee. + * Outputs: + * ar Activation record, all fields may be set. fp and oldfp + * are 0 if they cannot be extracted. return is 0 if the + * code cannot find a valid return address. args and arg0 + * are 0 if the number of arguments cannot be safely + * calculated. + * Returns: + * 1 if prologue is valid, 0 otherwise. + * Locking: + * None. + * Remarks: + * + * A prologue for ia32 generally looks like: + * + * pushl %ebp [All functions, but only if + * movl %esp, %ebp compiled with frame pointers] + * subl $auto, %esp [some functions] + * pushl %reg [some functions] + * pushl %reg [some functions] + * + * FIXME: Mike Galbraith says that gcc 2.95 can generate a slightly + * different prologue. No support for gcc 2.95 yet. + */ + +int +kdba_prologue(const kdb_symtab_t *symtab, kdb_machreg_t pc, kdb_machreg_t sp, + kdb_machreg_t fp, kdb_machreg_t ss, int caller, kdb_ar_t *ar) +{ + kdb_machreg_t ret_p, code = symtab->sym_start; + int oldfp_present = 0, unwound = 0; + + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf("kdba_prologue: code=0x%lx %s pc=0x%lx sp=0x%lx fp=0x%lx\n", + code, symtab->sym_name, pc, sp, fp); + } + + if (code == 0 || sp & 3 || ss != (sp & -8192)) + return(0); + + ar->end = sp; /* End of activation record +1 */ + + /* Special cases galore when the caller pc is within entry.S. + * The return address for these routines is outside the kernel, + * so the normal algorithm to find the frame does not work. + * Hand craft the frame to no setup, regs, locals etc, assume 6 + * parameters. + * This list was extracted from entry.S by looking for all call + * instructions that were eventually followed by RESTORE_ALL, + * take the label before each such instruction. + */ + if (caller && + (strcmp(symtab->sym_name, "lcall7") == 0 || + strcmp(symtab->sym_name, "lcall27") == 0 || + strcmp(symtab->sym_name, "kdb_call") == 0 || + strcmp(symtab->sym_name, "system_call") == 0 || + strcmp(symtab->sym_name, "tracesys") == 0 || + strcmp(symtab->sym_name, "signal_return") == 0 || + strcmp(symtab->sym_name, "v86_signal_return") == 0 || + strcmp(symtab->sym_name, "tracesys") == 0 || + strcmp(symtab->sym_name, "tracesys_exit") == 0 || + strcmp(symtab->sym_name, "handle_softirq") == 0 || + strcmp(symtab->sym_name, "reschedule") == 0 || + strcmp(symtab->sym_name, "error_code") == 0 || + strcmp(symtab->sym_name, "device_not_available") == 0 || + strcmp(symtab->sym_name, "nmi") == 0)) { + ar->start = ar->end + 6*4; /* 6 parameters */ + if ((ar->start & -8192) != ss) + ar->start = 0; + return(1); + } + + ar->setup = 4; /* Return address is always on stack */ + + /* Kludge. If we are sitting on 'ret' then the stack has been unwound, + * ignore all the startup code. + */ + if (kdba_getword(pc, 1) == 0xc3) { + /* ret */ + unwound = 1; + } + + if (!unwound + && code < pc + && kdba_getword(code, 1) == 0x55) { + /* pushl %ebp */ + ar->setup += 4; /* Frame pointer is on stack */ + oldfp_present = 1; + ++code; + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" pushl %%ebp\n"); + } + if (code < pc && + kdba_getword(code, 1) == 0x89 && + kdba_getword(code+1, 1) == 0xe5) { + /* movl %esp,%ebp */ + if (fp >= sp && (fp & -8192) == ss) + ar->fp = fp; /* %ebp has been set */ + code += 2; + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" movl %%esp,%%ebp, fp=0x%lx\n", ar->fp); + } + } + } + + if (!unwound && code < pc) { + if (kdba_getword(code, 1) == 0x83 && + kdba_getword(code+1, 1) == 0xec) { + /* subl $xx,%esp */ + code += 2; + ar->locals = kdba_getword(code, 1); + ++code; + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" subl $xx,%%esp, locals=%d\n", ar->locals); + } + } else if (kdba_getword(code, 1) == 0x81 && + kdba_getword(code+1, 1) == 0xec) { + /* subl $xxxxxxxx,%esp */ + code += 2; + ar->locals = kdba_getword(code, 4); + code += 4; + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" subl $xxxxxxxx,%%esp, locals=%d\n", ar->locals); + } + } + } + + while (!unwound && code < pc && (kdba_getword(code, 1)&0xf8) == 0x50) { + /* pushl %reg */ + ar->regs += 4; + ++code; + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" pushl %%reg, regs=%d\n", ar->regs); + } + } + + /* Check the return address. It must point within the kernel + * and the code at that location must be a valid entry sequence. + */ + if (ar->fp) { + ret_p = ar->fp + ar->setup; + } + else { + ret_p = ar->end + ar->regs + ar->locals + ar->setup; + } + ret_p -= 4; + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" ret_p(0)=0x%lx\n", ret_p); + } + if ((ret_p & -8192) == ss && + (ret_p = kdba_find_return(ret_p, ss, symtab))) { + ar->ret = kdba_getword(ret_p, 4); + } + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" ret_p(1)=0x%lx ret=0x%lx\n", ret_p, ar->ret); + } + if (ar->ret) { + ar->fp = ret_p - ar->setup + 4; /* "accurate" fp */ + ar->start = ret_p + 4; + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" fp=0x%lx start=0x%lx\n", ar->fp, ar->start); + } + } + if (oldfp_present) { + if (ar->fp) + ar->oldfp = kdba_getword(ar->fp, 4); + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" oldfp=0x%lx", ar->oldfp); + } + if (ar->oldfp <= ar->fp || (ar->oldfp & -8192) != ss) { + ar->oldfp = 0; + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf(" (out of range)"); + } + } + if (kdb_flags & KDB_DEBUG_ARA) { + kdb_printf("\n"); + } + } + return(1); +} + +void +kdba_installdbreg(kdb_bp_t *bp) +{ + kdb_machreg_t dr7; + + dr7 = kdb_getdr7(); + + kdb_putdr(bp->bp_hard->bph_reg, bp->bp_addr); + + dr7 |= DR7_GE; + + switch (bp->bp_hard->bph_reg){ + case 0: + DR7_RW0SET(dr7,bp->bp_hard->bph_mode); + DR7_LEN0SET(dr7,bp->bp_hard->bph_length); + DR7_G0SET(dr7); + break; + case 1: + DR7_RW1SET(dr7,bp->bp_hard->bph_mode); + DR7_LEN1SET(dr7,bp->bp_hard->bph_length); + DR7_G1SET(dr7); + break; + case 2: + DR7_RW2SET(dr7,bp->bp_hard->bph_mode); + DR7_LEN2SET(dr7,bp->bp_hard->bph_length); + DR7_G2SET(dr7); + break; + case 3: + DR7_RW3SET(dr7,bp->bp_hard->bph_mode); + DR7_LEN3SET(dr7,bp->bp_hard->bph_length); + DR7_G3SET(dr7); + break; + default: + kdb_printf("kdb: Bad debug register!! %ld\n", + bp->bp_hard->bph_reg); + break; + } + + kdb_putdr7(dr7); + return; +} + +void +kdba_removedbreg(kdb_bp_t *bp) +{ + int regnum; + kdb_machreg_t dr7; + + if (!bp->bp_hard) + return; + + regnum = bp->bp_hard->bph_reg; + + dr7 = kdb_getdr7(); + + kdb_putdr(regnum, 0); + + switch (regnum) { + case 0: + DR7_G0CLR(dr7); + DR7_L0CLR(dr7); + break; + case 1: + DR7_G1CLR(dr7); + DR7_L1CLR(dr7); + break; + case 2: + DR7_G2CLR(dr7); + DR7_L2CLR(dr7); + break; + case 3: + DR7_G3CLR(dr7); + DR7_L3CLR(dr7); + break; + default: + kdb_printf("kdb: Bad debug register!! %d\n", regnum); + break; + } + + kdb_putdr7(dr7); +} + +kdb_machreg_t +kdb_getdr6(void) +{ + return kdb_getdr(6); +} + +kdb_machreg_t +kdb_getdr7(void) +{ + return kdb_getdr(7); +} + +kdb_machreg_t +kdb_getdr(int regnum) +{ + kdb_machreg_t contents = 0; + switch(regnum) { + case 0: + __asm__ ("movl %%db0,%0\n\t":"=r"(contents)); + break; + case 1: + __asm__ ("movl %%db1,%0\n\t":"=r"(contents)); + break; + case 2: + __asm__ ("movl %%db2,%0\n\t":"=r"(contents)); + break; + case 3: + __asm__ ("movl %%db3,%0\n\t":"=r"(contents)); + break; + case 4: + case 5: + break; + case 6: + __asm__ ("movl %%db6,%0\n\t":"=r"(contents)); + break; + case 7: + __asm__ ("movl %%db7,%0\n\t":"=r"(contents)); + break; + default: + break; + } + + return contents; +} + + +kdb_machreg_t +kdb_getcr(int regnum) +{ + kdb_machreg_t contents = 0; + switch(regnum) { + case 0: + __asm__ ("movl %%cr0,%0\n\t":"=r"(contents)); + break; + case 1: + break; + case 2: + __asm__ ("movl %%cr2,%0\n\t":"=r"(contents)); + break; + case 3: + __asm__ ("movl %%cr3,%0\n\t":"=r"(contents)); + break; + case 4: + __asm__ ("movl %%cr4,%0\n\t":"=r"(contents)); + break; + default: + break; + } + + return contents; +} + +void +kdb_putdr6(kdb_machreg_t contents) +{ + kdb_putdr(6, contents); +} + +void +kdb_putdr7(kdb_machreg_t contents) +{ + kdb_putdr(7, contents); +} + +void +kdb_putdr(int regnum, kdb_machreg_t contents) +{ + switch(regnum) { + case 0: + __asm__ ("movl %0,%%db0\n\t"::"r"(contents)); + break; + case 1: + __asm__ ("movl %0,%%db1\n\t"::"r"(contents)); + break; + case 2: + __asm__ ("movl %0,%%db2\n\t"::"r"(contents)); + break; + case 3: + __asm__ ("movl %0,%%db3\n\t"::"r"(contents)); + break; + case 4: + case 5: + break; + case 6: + __asm__ ("movl %0,%%db6\n\t"::"r"(contents)); + break; + case 7: + __asm__ ("movl %0,%%db7\n\t"::"r"(contents)); + break; + default: + break; + } +} + +/* + * kdba_getregcontents + * + * Return the contents of the register specified by the + * input string argument. Return an error if the string + * does not match a machine register. + * + * The following pseudo register names are supported: + * ®s - Prints address of exception frame + * kesp - Prints kernel stack pointer at time of fault + * % - Uses the value of the registers at the + * last time the user process entered kernel + * mode, instead of the registers at the time + * kdb was entered. + * + * Parameters: + * regname Pointer to string naming register + * regs Pointer to structure containing registers. + * Outputs: + * *contents Pointer to unsigned long to recieve register contents + * Returns: + * 0 Success + * KDB_BADREG Invalid register name + * Locking: + * None. + * Remarks: + * If kdb was entered via an interrupt from the kernel itself then + * ss and esp are *not* on the stack. + */ + +static struct kdbregs { + char *reg_name; + size_t reg_offset; +} kdbreglist[] = { + { "eax", offsetof(struct pt_regs, eax) }, + { "ebx", offsetof(struct pt_regs, ebx) }, + { "ecx", offsetof(struct pt_regs, ecx) }, + { "edx", offsetof(struct pt_regs, edx) }, + + { "esi", offsetof(struct pt_regs, esi) }, + { "edi", offsetof(struct pt_regs, edi) }, + { "esp", offsetof(struct pt_regs, esp) }, + { "eip", offsetof(struct pt_regs, eip) }, + + { "ebp", offsetof(struct pt_regs, ebp) }, + { " 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); + +static struct kdbregs dbreglist[] = { + { "dr0", 0 }, + { "dr1", 1 }, + { "dr2", 2 }, + { "dr3", 3 }, + { "dr6", 6 }, + { "dr7", 7 }, +}; + +static const int ndbreglist = sizeof(dbreglist) / sizeof(struct kdbregs); + +int +kdba_getregcontents(const char *regname, + struct pt_regs *regs, + kdb_machreg_t *contents) +{ + int i; + + if (strcmp(regname, "®s") == 0) { + *contents = (unsigned long)regs; + return 0; + } + + if (strcmp(regname, "kesp") == 0) { + *contents = (unsigned long)regs + sizeof(struct pt_regs); + if ((regs->xcs & 0xffff) == __KERNEL_CS) { + /* esp and ss are not on stack */ + *contents -= 2*4; + } + return 0; + } + + if (regname[0] == '%') { + /* User registers: %%e[a-c]x, etc */ + regname++; + regs = (struct pt_regs *) + (current->thread.esp0 - sizeof(struct pt_regs)); + } + + for (i=0; ixcs & 0xffff) == __KERNEL_CS) { + /* No cpl switch, esp and ss are not on stack */ + if (strcmp(kdbreglist[i].reg_name, "esp") == 0) { + *contents = (kdb_machreg_t)regs + + sizeof(struct pt_regs) - 2*4; + return(0); + } + if (strcmp(kdbreglist[i].reg_name, " ss") == 0) { + asm volatile( + "pushl %%ss\n" + "popl %0\n" + :"=m" (*contents)); + return(0); + } + } + *contents = *(unsigned long *)((unsigned long)regs + + kdbreglist[i].reg_offset); + return(0); + } + + for (i=0; i + * + * Parameters: + * regname Pointer to string naming register + * regs Pointer to structure containing registers. + * contents Unsigned long containing new register contents + * Outputs: + * Returns: + * 0 Success + * KDB_BADREG Invalid register name + * Locking: + * None. + * Remarks: + */ + +int +kdba_setregcontents(const char *regname, + struct pt_regs *regs, + unsigned long contents) +{ + int i; + + if (regname[0] == '%') { + regname++; + regs = (struct pt_regs *) + (current->thread.esp0 - sizeof(struct pt_regs)); + } + + for (i=0; ithread.esp0 - sizeof(struct pt_regs)); + } + + if (type == NULL) { + struct kdbregs *rlp; + kdb_machreg_t contents; + + for (i=0, rlp=kdbreglist; ieip; +} + +int +kdba_setpc(kdb_eframe_t ef, kdb_machreg_t newpc) +{ + ef->eip = newpc; + return 0; +} + +void +kdba_disableint(kdb_intstate_t *state) +{ + int *fp = (int *)state; + int flags; + + __save_flags(flags); + __cli(); + + *fp = flags; +} + +void +kdba_restoreint(kdb_intstate_t *state) +{ + int flags = *(int *)state; + __restore_flags(flags); +} + +void +kdba_setsinglestep(struct pt_regs *regs) +{ + regs->eflags |= EF_TF; +} + +int +kdb_getcurrentframe(struct pt_regs *regs) +{ + regs->xcs = 0; +#if defined(CONFIG_FRAME_POINTER) + asm volatile("movl %%ebp,%0":"=m" (*(int *)®s->ebp)); +#endif + asm volatile("movl %%esp,%0":"=m" (*(int *)®s->esp)); + + return 0; +} + +int +kdb_setjmp(kdb_jmp_buf *jb) +{ +#if defined(CONFIG_FRAME_POINTER) + __asm__ ("movl 8(%esp), %eax\n\t" + "movl %ebx, 0(%eax)\n\t" + "movl %esi, 4(%eax)\n\t" + "movl %edi, 8(%eax)\n\t" + "movl (%esp), %ecx\n\t" + "movl %ecx, 12(%eax)\n\t" + "leal 8(%esp), %ecx\n\t" + "movl %ecx, 16(%eax)\n\t" + "movl 4(%esp), %ecx\n\t" + "movl %ecx, 20(%eax)\n\t"); +#else /* CONFIG_FRAME_POINTER */ + __asm__ ("movl 4(%esp), %eax\n\t" + "movl %ebx, 0(%eax)\n\t" + "movl %esi, 4(%eax)\n\t" + "movl %edi, 8(%eax)\n\t" + "movl %ebp, 12(%eax)\n\t" + "leal 4(%esp), %ecx\n\t" + "movl %ecx, 16(%eax)\n\t" + "movl 0(%esp), %ecx\n\t" + "movl %ecx, 20(%eax)\n\t"); +#endif /* CONFIG_FRAME_POINTER */ + return 0; +} + +void +kdb_longjmp(kdb_jmp_buf *jb, int reason) +{ +#if defined(CONFIG_FRAME_POINTER) + __asm__("movl 8(%esp), %ecx\n\t" + "movl 12(%esp), %eax\n\t" + "movl 20(%ecx), %edx\n\t" + "movl 0(%ecx), %ebx\n\t" + "movl 4(%ecx), %esi\n\t" + "movl 8(%ecx), %edi\n\t" + "movl 12(%ecx), %ebp\n\t" + "movl 16(%ecx), %esp\n\t" + "jmp *%edx\n"); +#else /* CONFIG_FRAME_POINTER */ + __asm__("movl 4(%esp), %ecx\n\t" + "movl 8(%esp), %eax\n\t" + "movl 20(%ecx), %edx\n\t" + "movl 0(%ecx), %ebx\n\t" + "movl 4(%ecx), %esi\n\t" + "movl 8(%ecx), %edi\n\t" + "movl 12(%ecx), %ebp\n\t" + "movl 16(%ecx), %esp\n\t" + "jmp *%edx\n"); +#endif /* CONFIG_FRAME_POINTER */ +} + + +/* + * kdba_enable_mce + * + * This function is called once on each CPU to enable machine + * check exception handling. + * + * Inputs: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + */ + +void +kdba_enable_mce(void) +{ + /* + * Enable Machine Check Exceptions + */ + u32 x86_capability; + + x86_capability = boot_cpu_data.x86_capability; + if ((x86_capability & X86_FEATURE_MCE) && + (x86_capability & X86_FEATURE_MCA)) { + u32 i, lv, hv, count; + rdmsr(MCG_CAP, lv, hv); + count = lv&0xff; + if (lv & 0x100) { + hv = lv = 0xffffffff; + wrmsr(MCG_CTL, lv, hv); + } + for(i=1; i (unsigned long)high_memory) { + 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) { + kdb_printf("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; + } + } + + kdb_printf("kdbgetword: Bad width\n"); + return 0L; +} + +/* + * kdba_putword + * + * Architecture specific function to access kernel virtual + * address space. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +unsigned long +kdba_putword(unsigned long addr, size_t size, unsigned long contents) +{ + /* + * This function checks the address for validity. Any address + * in the range PAGE_OFFSET to high_memory is legal, any address + * which maps to a vmalloc region is legal, and any address which + * is a user address, we use get_user() to verify validity. + */ + + if (addr < PAGE_OFFSET) { + /* + * Usermode address. + */ + unsigned long diag; + + switch (size) { + case 4: + { unsigned long *lp; + + lp = (unsigned long *) addr; + diag = put_user(contents, lp); + break; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *) addr; + diag = put_user(contents, sp); + break; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *) addr; + diag = put_user(contents, cp); + break; + } + default: + kdb_printf("kdba_putword: Bad width\n"); + return 0; + } + + if (diag) { + if ((kdb_flags & KDB_FLAG_SUPRESS) == 0) { + kdb_printf("kdb: Bad user address 0x%lx\n", addr); + kdb_flags |= KDB_FLAG_SUPRESS; + } + return 0; + } + kdb_flags &= ~KDB_FLAG_SUPRESS; + return 0; + } + + if (addr > (unsigned long)high_memory) { + extern int kdb_vmlist_check(unsigned long, unsigned long); + + if (!kdb_vmlist_check(addr, addr+size)) { + /* + * Would appear to be an illegal kernel address; + * Print a message once, and don't print again until + * a legal address is used. + */ + if ((kdb_flags & KDB_FLAG_SUPRESS) == 0) { + kdb_printf("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 (size) { + case 4: + { unsigned long *lp; + + lp = (unsigned long *)(addr); + *lp = contents; + return 0; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *)(addr); + *sp = (unsigned short) contents; + return 0; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *)(addr); + *cp = (unsigned char) contents; + return 0; + } + } + + kdb_printf("kdba_putword: Bad width\n"); + return 0; +} + +/* + * kdba_callback_die + * + * Callback function for kernel 'die' function. + * + * Parameters: + * regs Register contents at time of trap + * error_code Trap-specific error code value + * trapno Trap number + * vp Pointer to die message + * Returns: + * Returns 1 if fault handled by kdb. + * Locking: + * None. + * Remarks: + * + */ +int +kdba_callback_die(struct pt_regs *regs, int error_code, long trapno, void *vp) +{ + /* + * Save a pointer to the message provided to 'die()'. + */ + kdb_diemsg = (char *)vp; + + return kdb(KDB_REASON_PANIC, error_code, (kdb_eframe_t) regs); +} + +/* + * kdba_callback_bp + * + * Callback function for kernel breakpoint trap. + * + * Parameters: + * regs Register contents at time of trap + * error_code Trap-specific error code value + * trapno Trap number + * vp Not Used. + * Returns: + * Returns 1 if fault handled by kdb. + * Locking: + * None. + * Remarks: + * + */ + +int +kdba_callback_bp(struct pt_regs *regs, int error_code, long trapno, void *vp) +{ + int diag; + + if (kdb_flags & KDB_DEBUG_BP) + kdb_printf("cb_bp: e_c = %d tn = %ld regs = 0x%p\n", error_code, + trapno, regs); + + diag = kdb(KDB_REASON_BREAK, error_code, (kdb_eframe_t) regs); + + if (kdb_flags & KDB_DEBUG_BP) + kdb_printf("cb_bp: e_c = %d tn = %ld regs = 0x%p diag = %d\n", error_code, + trapno, regs, diag); + return diag; +} + +/* + * kdba_callback_debug + * + * Callback function for kernel debug register trap. + * + * Parameters: + * regs Register contents at time of trap + * error_code Trap-specific error code value + * trapno Trap number + * vp Not used. + * Returns: + * Returns 1 if fault handled by kdb. + * Locking: + * None. + * Remarks: + * + */ + +int +kdba_callback_debug(struct pt_regs *regs, int error_code, long trapno, void *vp) +{ + return kdb(KDB_REASON_DEBUG, error_code, (kdb_eframe_t) regs); +} + +/* + * kdba_init + * + * Architecture specific initialization. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +void +kdba_init(void) +{ + kdba_enable_lbr(); + + return; +} diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kernel/entry.S Tue Jun 20 03:24:39 2000 +++ linux/arch/i386/kernel/entry.S Tue Jun 13 16:30:11 2000 @@ -185,6 +185,18 @@ jne tracesys_exit jmp ret_from_sys_call +#if defined(CONFIG_KDB) +ENTRY(kdb_call) + pushl %eax # save orig EAX + SAVE_ALL + pushl %esp # struct pt_regs + pushl $0 # error_code + pushl $7 # KDB_REASON_INT + call SYMBOL_NAME(kdb) + addl $12,%esp # remove args + RESTORE_ALL +#endif + /* * Return to user mode is not as complex as all this looks, * but we want the default path for a system call return to @@ -343,6 +355,13 @@ addl $4,%esp ret +#if defined(CONFIG_KDB) +ENTRY(machine_check) + pushl $0 + pushl $ SYMBOL_NAME(do_machine_check) + jmp error_code +#endif /* CONFIG_KDB */ + ENTRY(debug) pushl $0 pushl $ SYMBOL_NAME(do_debug) @@ -383,10 +402,12 @@ pushl $ SYMBOL_NAME(do_coprocessor_segment_overrun) jmp error_code +#if !defined(CONFIG_KDB) ENTRY(reserved) pushl $0 pushl $ SYMBOL_NAME(do_reserved) jmp error_code +#endif ENTRY(double_fault) pushl $ SYMBOL_NAME(do_double_fault) @@ -411,6 +432,22 @@ ENTRY(alignment_check) pushl $ SYMBOL_NAME(do_alignment_check) jmp error_code + +#if defined(CONFIG_KDB) +ENTRY(page_fault_mca) + pushl %ecx + pushl %edx + pushl %eax + movl $473,%ecx + rdmsr + andl $0xfffffffe,%eax /* Disable last branch recording */ + wrmsr + popl %eax + popl %edx + popl %ecx + pushl $ SYMBOL_NAME(do_page_fault) + jmp error_code +#endif ENTRY(page_fault) pushl $ SYMBOL_NAME(do_page_fault) diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kernel/i386_ksyms.c Sat Apr 22 01:27:21 2000 +++ linux/arch/i386/kernel/i386_ksyms.c Tue Jun 13 16:30:11 2000 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -130,6 +131,22 @@ #ifdef CONFIG_VT EXPORT_SYMBOL(screen_info); +#endif + +#if defined(CONFIG_KDB) +EXPORT_SYMBOL(kdb_register); +EXPORT_SYMBOL(kdb_unregister); +EXPORT_SYMBOL(kdba_getword); +EXPORT_SYMBOL(kdba_putword); +EXPORT_SYMBOL(kdbgetularg); +EXPORT_SYMBOL(kdbgetenv); +EXPORT_SYMBOL(kdbgetintenv); +EXPORT_SYMBOL(kdbgetaddrarg); +EXPORT_SYMBOL(kdb); +EXPORT_SYMBOL(kdbgetsymval); +EXPORT_SYMBOL(kdbnearsym); +EXPORT_SYMBOL(kdb_printf); +EXPORT_SYMBOL(kdb_symbol_print); #endif EXPORT_SYMBOL(get_wchan); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kernel/i8259.c linux/arch/i386/kernel/i8259.c --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kernel/i8259.c Tue Jun 20 03:24:39 2000 +++ linux/arch/i386/kernel/i8259.c Fri Jun 16 00:14:33 2000 @@ -445,7 +445,11 @@ */ for (i = 0; i < NR_IRQS; i++) { int vector = FIRST_EXTERNAL_VECTOR + i; - if (vector != SYSCALL_VECTOR) + if ((vector != SYSCALL_VECTOR) +#if defined(CONFIG_KDB) + && (vector != KDBENTER_VECTOR) +#endif + ) set_intr_gate(vector, interrupt[i]); } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kernel/process.c Tue Jun 20 03:24:39 2000 +++ linux/arch/i386/kernel/process.c Fri Jun 16 00:14:33 2000 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -306,7 +307,16 @@ * Stop all CPUs and turn off local APICs and the IO-APIC, so * other OSs see a clean IRQ state. */ +#if defined(CONFIG_KDB) + /* + * If this restart is occuring while kdb is running (e.g. reboot + * command), the other CPU's are already stopped. Don't try to + * stop them yet again. + */ + if (!kdb_active) +#endif smp_send_stop(); + disable_IO_APIC(); #endif diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kernel/semaphore.c linux/arch/i386/kernel/semaphore.c --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kernel/semaphore.c Thu Mar 23 03:12:20 2000 +++ linux/arch/i386/kernel/semaphore.c Mon Jun 19 17:20:02 2000 @@ -182,6 +182,10 @@ ".align 4\n" ".globl __down_failed\n" "__down_failed:\n\t" +#if defined(CONFIG_FRAME_POINTER) + "pushl %ebp\n\t" + "movl %esp,%ebp\n\t" +#endif "pushl %eax\n\t" "pushl %edx\n\t" "pushl %ecx\n\t" @@ -189,6 +193,10 @@ "popl %ecx\n\t" "popl %edx\n\t" "popl %eax\n\t" +#if defined(CONFIG_FRAME_POINTER) + "movl %ebp,%esp\n\t" + "popl %ebp\n\t" +#endif "ret" ); @@ -196,11 +204,19 @@ ".align 4\n" ".globl __down_failed_interruptible\n" "__down_failed_interruptible:\n\t" +#if defined(CONFIG_FRAME_POINTER) + "pushl %ebp\n\t" + "movl %esp,%ebp\n\t" +#endif "pushl %edx\n\t" "pushl %ecx\n\t" "call __down_interruptible\n\t" "popl %ecx\n\t" "popl %edx\n\t" +#if defined(CONFIG_FRAME_POINTER) + "movl %ebp,%esp\n\t" + "popl %ebp\n\t" +#endif "ret" ); @@ -208,11 +224,19 @@ ".align 4\n" ".globl __down_failed_trylock\n" "__down_failed_trylock:\n\t" +#if defined(CONFIG_FRAME_POINTER) + "pushl %ebp\n\t" + "movl %esp,%ebp\n\t" +#endif "pushl %edx\n\t" "pushl %ecx\n\t" "call __down_trylock\n\t" "popl %ecx\n\t" "popl %edx\n\t" +#if defined(CONFIG_FRAME_POINTER) + "movl %ebp,%esp\n\t" + "popl %ebp\n\t" +#endif "ret" ); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kernel/smp.c Thu May 25 00:46:10 2000 +++ linux/arch/i386/kernel/smp.c Tue Jun 13 16:30:11 2000 @@ -21,6 +21,10 @@ #include #include +#if defined(CONFIG_KDB) +#include +#endif + /* * Some notes on x86 processor bugs affecting SMP operation: * @@ -142,6 +146,15 @@ */ cfg = __prepare_ICR(shortcut, vector); +#if defined(CONFIG_KDB) + if (vector == KDB_VECTOR) { + /* + * Setup KDB IPI to be delivered as an NMI + */ + cfg = (cfg&~APIC_VECTOR_MASK)|APIC_DM_NMI; + } +#endif /* CONFIG_KDB */ + /* * Send the IPI. The write to APIC_ICR fires this off. */ @@ -376,6 +389,16 @@ do_flush_tlb_all_local(); } + +#if defined(CONFIG_KDB) +void +smp_kdb_stop(void) +{ + smp_kdb_wait = 0xffffffff; + clear_bit(smp_processor_id(), &smp_kdb_wait); + send_IPI_allbutself(KDB_VECTOR); +} +#endif /* CONFIG_KDB */ /* * this function sends a 'reschedule' IPI to another CPU. diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c --- /opt/kernel/linux-2.4.0-test1-ac21/arch/i386/kernel/traps.c Tue Jun 20 03:24:39 2000 +++ linux/arch/i386/kernel/traps.c Fri Jun 16 00:14:33 2000 @@ -31,6 +31,11 @@ #include #endif +#if defined(CONFIG_KDB) +#include +#include +#endif + #include #include #include @@ -50,6 +55,9 @@ #include asmlinkage int system_call(void); +#if defined(CONFIG_KDB) +asmlinkage int kdb_call(void); +#endif asmlinkage void lcall7(void); asmlinkage void lcall27(void); @@ -153,6 +161,9 @@ asmlinkage void stack_segment(void); asmlinkage void general_protection(void); asmlinkage void page_fault(void); +#if defined(CONFIG_KDB) +asmlinkage void page_fault_mca(void); +#endif asmlinkage void coprocessor_error(void); #ifdef CONFIG_X86_XMM asmlinkage void simd_coprocessor_error(void); @@ -267,11 +278,163 @@ printk("\n"); } +#if defined(CONFIG_KDB) +spinlock_t dblist_lock = SPIN_LOCK_UNLOCKED; + +#define MAXDBLIST 8 + +typedef int (*dbfunc_t)(struct pt_regs * regs, int error_code, + long trap, void *value); + +typedef struct __db_list_s { + struct __db_list_s *db_next; + dbfunc_t db_func; +} dblist_t; + +typedef struct __db_listhead_s { + dblist_t dblh_list[MAXDBLIST]; + dblist_t *dblh_head; + char *dblh_name; +} dblisthead_t; + + /* + * Hook-up list to 'die' function + */ +static dblisthead_t dblist_die = + { {{ NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }}, + NULL, + "die" + }; + + /* + * Hook-up list to int3 + */ +static dblisthead_t dblist_int3 = + { {{ NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }}, + NULL, + "int3" + }; + + /* + * Hook-up list to debug trap + */ +static dblisthead_t dblist_debug = + { {{ NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }}, + NULL, + "debug" + }; + +static void register_dbfunc(dblisthead_t *lhp, dbfunc_t dfp) +{ + int i; + + spin_lock(&dblist_lock); + +#if defined(KDBDEBUG) + kdb_printf("listhead 0x%p func 0x%p\n", lhp, dfp); +#endif + + for(i=0; idblh_list[i].db_func == NULL) { + break; + } + } + if (i == MAXDBLIST) { +#if defined(KDBDEBUG) + kdb_printf("register_dbfunc: 0x%p not registered for %s\n", + dfp, lhp->dblh_name); +#endif + spin_unlock(&dblist_lock); + return; + } + + lhp->dblh_list[i].db_func = dfp; + lhp->dblh_list[i].db_next = lhp->dblh_head; + lhp->dblh_head = &lhp->dblh_list[i]; + + spin_unlock(&dblist_lock); +} + +void register_die(dbfunc_t dfp) +{ + register_dbfunc(&dblist_die, dfp); +} + +void register_int3(dbfunc_t dfp) +{ + register_dbfunc(&dblist_int3, dfp); +} + +void register_debug(dbfunc_t dfp) +{ + register_dbfunc(&dblist_debug, dfp); +} + +static inline int +callout_dbfunc(dblisthead_t *lhp, struct pt_regs *regs, int error_code, + long trap_number, void *parameter) +{ + dblist_t *dlp = lhp->dblh_head; + int diag = 0; + + spin_lock(&dblist_lock); +#if defined(KDBDEBUG) + kdb_printf("callout dbfunc: 0x%p\n", lhp); +#endif + while (dlp) { + int rv; + + /* + * The first callout function to handle this callout + * condition will return '1'. No other callout handlers + * will be invoked. + */ + rv = dlp->db_func(regs, error_code, trap_number, parameter); +#if defined(KDBDEBUG) + kdb_printf("callout 0x%p diag %d\n", dlp->db_func, rv); +#endif + if (rv) { + diag ++; + break; + } + + dlp = dlp->db_next; + } + spin_unlock(&dblist_lock); + + return diag; +} +#endif + spinlock_t die_lock = SPIN_LOCK_UNLOCKED; void die(const char * str, struct pt_regs * regs, long err) { console_verbose(); +#if defined(CONFIG_KDB) + (void) callout_dbfunc(&dblist_die, regs, err, -1, (void *)str); +#endif spin_lock_irq(&die_lock); printk("%s: %04lx\n", str, err & 0xffff); show_registers(regs); @@ -310,7 +473,9 @@ } DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, current, FPE_INTDIV, regs->eip) +#if !defined(CONFIG_KDB) DO_VM86_ERROR( 3, SIGTRAP, "int3", int3, current) +#endif DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow, current) DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds, current) DO_ERROR_INFO( 6, SIGILL, "invalid operand", invalid_op, current, ILL_ILLOPN, regs->eip) @@ -321,7 +486,9 @@ DO_ERROR(11, SIGBUS, "segment not present", segment_not_present, current) DO_ERROR(12, SIGBUS, "stack segment", stack_segment, current) DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, current, BUS_ADRALN, get_cr2()) +#if !defined(CONFIG_KDB) DO_ERROR(18, SIGSEGV, "reserved", reserved, current) +#endif /* CONFIG_KDB */ asmlinkage void do_general_protection(struct pt_regs * regs, long error_code) { @@ -390,6 +557,9 @@ return; } #endif +#if defined(CONFIG_KDB) + kdb(KDB_REASON_NMI, reason, regs); +#endif printk("Uhhuh. NMI received for unknown reason %02x.\n", reason); printk("Dazed and confused, but trying to continue\n"); printk("Do you have a strange power saving mode enabled?\n"); @@ -397,7 +567,11 @@ #if CONFIG_X86_IO_APIC +#if defined(CONFIG_KDB) +int nmi_watchdog = 0; +#else int nmi_watchdog = 1; +#endif static int __init setup_nmi_watchdog(char *str) { @@ -466,12 +640,29 @@ } #endif +#if defined(CONFIG_SMP) && defined(CONFIG_KDB) +static void +do_ack_apic_irq(void) +{ + ack_APIC_irq(); +} +#endif + asmlinkage void do_nmi(struct pt_regs * regs, long error_code) { unsigned char reason = inb(0x61); atomic_inc(&nmi_counter(smp_processor_id())); +#if defined(CONFIG_SMP) && defined(CONFIG_KDB) + /* + * Call the kernel debugger to see if this NMI is due + * to an KDB requested IPI. If so, kdb will handle it. + */ + if (kdb_ipi((kdb_eframe_t)regs, do_ack_apic_irq)) { + return; + } +#endif if (!(reason & 0xc0)) { #if CONFIG_X86_IO_APIC /* @@ -502,6 +693,29 @@ inb(0x71); /* dummy */ } +#if defined(CONFIG_KDB) +asmlinkage void do_machine_check(struct pt_regs * regs, long error_code) +{ + u32 i, lv, hv, x86_capability; + + x86_capability = boot_cpu_data.x86_capability; + if ((x86_capability & X86_FEATURE_MCE) && + (x86_capability & X86_FEATURE_MCA)) { + rdmsr(MCG_STATUS_MSR, lv, hv); + kdb_printf("machine check status 0x%x\n", lv); + rdmsr(MCG_CAP, lv, hv); + kdb_printf("%d units \n", lv&0xff); + for(i=0; i < (lv&0xff); i++) { + u32 nlv, nhv; + rdmsr(MC0_BASE+MC0_STATUS_OFFSET+(i*MC0_BANK_COUNT), nlv, nhv); + kdb_printf("status %d is 0x%x 0x%x\n", i, nlv, nhv); + } + } + + kdb(KDB_REASON_NMI, error_code, regs); +} +#endif + /* * Careful - we must not do a lock-kernel until we have checked that the * debug fault happened in user mode. Getting debug exceptions while @@ -568,6 +782,14 @@ return; clear_dr7: +#if defined(CONFIG_KDB) + /* + * The callout functions will return 'true' if they've handled + * the callout condition. + */ + if (callout_dbfunc(&dblist_debug, regs, error_code, SIGTRAP, NULL)) + return; +#endif __asm__("movl %0,%%db7" : /* no output */ : "r" (0)); @@ -578,6 +800,27 @@ return; } +#if defined(CONFIG_KDB) +asmlinkage void do_int3(struct pt_regs * regs, long error_code) +{ + if (callout_dbfunc(&dblist_int3, regs, error_code, SIGTRAP, NULL)) + return; + + lock_kernel(); + if (regs->eflags & VM_MASK) { + if (!handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, 3)) + goto out; + /* else fall through */ + } + current->thread.error_code = error_code; + current->thread.trap_no = 3; + force_sig(SIGTRAP, current); + die_if_kernel("INT3",regs,error_code); +out: + unlock_kernel(); +} +#endif + /* * Note that we play around with the 'TS' bit in an attempt to get * the correct behaviour even in the presence of the asynchronous @@ -959,7 +1202,17 @@ set_trap_gate(11,&segment_not_present); set_trap_gate(12,&stack_segment); set_trap_gate(13,&general_protection); +#if defined(CONFIG_KDB) + if ((boot_cpu_data.x86_capability & X86_FEATURE_MCE) && + (boot_cpu_data.x86_capability & X86_FEATURE_MCA)) { + set_trap_gate(14,&page_fault_mca); + } + else { + set_trap_gate(14,&page_fault); + } +#else set_trap_gate(14,&page_fault); +#endif set_trap_gate(15,&spurious_interrupt_bug); set_trap_gate(16,&coprocessor_error); set_trap_gate(17,&alignment_check); @@ -968,6 +1221,18 @@ set_trap_gate(19,&simd_coprocessor_error); #endif set_system_gate(SYSCALL_VECTOR,&system_call); +#if defined(CONFIG_KDB) + { + extern void *machine_check; + set_trap_gate(18, &machine_check); + } + kdb_enablehwfault(); + /* + * A trap gate, used by the kernel to enter the + * debugger, preserving all registers. + */ + set_trap_gate(KDBENTER_VECTOR, &kdb_call); +#endif /* CONFIG_KDB */ /* * default LDT is a single-entry callgate to lcall7 for iBCS @@ -985,5 +1250,11 @@ superio_init(); lithium_init(); cobalt_init(); +#endif + +#if defined(CONFIG_KDB) + register_die(kdba_callback_die); + register_int3(kdba_callback_bp); + register_debug(kdba_callback_debug); #endif } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/m68k/vmlinux-sun3.lds linux/arch/m68k/vmlinux-sun3.lds --- /opt/kernel/linux-2.4.0-test1-ac21/arch/m68k/vmlinux-sun3.lds Sat Sep 4 22:06:41 1999 +++ linux/arch/m68k/vmlinux-sun3.lds Tue Jun 13 16:30:11 2000 @@ -29,6 +29,9 @@ __start___ksymtab = .; /* Kernel symbol table */ *(__ksymtab) __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + *(__kallsyms) + __stop___kallsyms = .; } /* End of data goes *here* so that freeing init code works properly. */ _edata = .; diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/sh/vmlinux.lds.S linux/arch/sh/vmlinux.lds.S --- /opt/kernel/linux-2.4.0-test1-ac21/arch/sh/vmlinux.lds.S Tue Jun 20 03:24:39 2000 +++ linux/arch/sh/vmlinux.lds.S Sat Jun 17 23:43:53 2000 @@ -36,6 +36,10 @@ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + _etext = .; /* End of text section */ .data : { /* Data */ diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/arch/sparc64/config.in linux/arch/sparc64/config.in --- /opt/kernel/linux-2.4.0-test1-ac21/arch/sparc64/config.in Tue Jun 20 03:24:39 2000 +++ linux/arch/sparc64/config.in Tue Jun 13 16:24:32 2000 @@ -97,6 +97,7 @@ bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD fi +tristate 'Packet writing on CD/DVD media' CONFIG_CDROM_PKTCDVD tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP tristate 'Network block device support' CONFIG_BLK_DEV_NBD diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/block/Config.in linux/drivers/block/Config.in --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/block/Config.in Tue Jun 20 03:24:39 2000 +++ linux/drivers/block/Config.in Tue Jun 13 21:19:29 2000 @@ -36,6 +36,7 @@ dep_tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA $CONFIG_PCI dep_tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960 $CONFIG_PCI +tristate 'Packet writing on CD/DVD media' CONFIG_CDROM_PKTCDVD tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/block/DAC960.c linux/drivers/block/DAC960.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/block/DAC960.c Sat May 13 16:35:38 2000 +++ linux/drivers/block/DAC960.c Tue Jun 13 15:56:25 2000 @@ -1473,9 +1473,8 @@ Command->SegmentCount = Request->nr_segments; Command->BufferHeader = Request->bh; RequestBuffer = Request->buffer; - Request->rq_status = RQ_INACTIVE; blkdev_dequeue_request(Request); - wake_up(&wait_for_request); + blkdev_release_request(Request); if (Command->SegmentCount == 1) { DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/block/Makefile linux/drivers/block/Makefile --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/block/Makefile Tue Jun 20 03:24:39 2000 +++ linux/drivers/block/Makefile Mon Jun 19 01:05:27 2000 @@ -52,6 +52,8 @@ obj-$(CONFIG_BLK_DEV_NBD) += nbd.o +obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o + ifeq ($(CONFIG_PARIDE),y) SUB_DIRS += paride MOD_IN_SUB_DIRS += paride diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/block/elevator.c linux/drivers/block/elevator.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/block/elevator.c Sat May 20 20:36:59 2000 +++ linux/drivers/block/elevator.c Tue Jun 13 17:24:54 2000 @@ -4,6 +4,16 @@ * Block device elevator/IO-scheduler. * * Copyright (C) 2000 Andrea Arcangeli SuSE + * + * 30042000 Jens Axboe : + * + * Split the elevator a bit so that it is possible to choose a different + * one or even write a new "plug in". There are three pieces: + * - elevator_fn, inserts a new request in the queue list + * - elevator_merge_fn, decides whether a new buffer can be merged with + * an existing request + * - elevator_dequeue_fn, called when a request is taken off the active list + * */ #include @@ -12,9 +22,9 @@ #include #include -static void elevator_default(struct request * req, elevator_t * elevator, - struct list_head * real_head, - struct list_head * head, int orig_latency) +void elevator_default(struct request *req, elevator_t * elevator, + struct list_head * real_head, + struct list_head * head, int orig_latency) { struct list_head * entry = real_head, * point = NULL; struct request * tmp; @@ -22,6 +32,12 @@ int latency = orig_latency -= elevator->nr_segments, pass = 0; int point_latency = 0xbeefbeef; + if (list_empty(real_head)) { + req->elevator_sequence = elevator_sequence(elevator, orig_latency); + list_add(&req->queue, real_head); + return; + } + while ((entry = entry->prev) != head) { if (!point && latency >= 0) { point = entry; @@ -49,19 +65,130 @@ req->elevator_sequence = elevator_sequence(elevator, latency); } +int elevator_default_merge(request_queue_t *q, struct request **req, + struct buffer_head *bh, int rw, + int *max_sectors, int *max_segments) +{ + struct list_head *entry, *head = &q->queue_head; + unsigned int count = bh->b_size >> 9; + elevator_t *elevator = &q->elevator; + int orig_latency, latency, sequence, action, starving = 0; + + /* + * Avoid write-bombs as not to hurt interactiveness of reads + */ + if (rw == WRITE) + *max_segments = elevator->max_bomb_segments; + + latency = orig_latency = elevator_request_latency(elevator, rw); + sequence = elevator->sequence; + + if (q->head_active && !q->plugged) + head = head->next; + + entry = head; + while ((entry = entry->prev) != head && !starving) { + *req = blkdev_entry_to_request(entry); + latency += (*req)->nr_segments; + if (elevator_sequence_before((*req)->elevator_sequence, sequence)) + starving = 1; + if (latency < 0) + continue; + if ((*req)->sem) + continue; + if ((*req)->cmd != rw) + continue; + if ((*req)->nr_sectors + count > *max_sectors) + continue; + if ((*req)->rq_dev != bh->b_rdev) + continue; + if ((*req)->sector + (*req)->nr_sectors == bh->b_rsector) { + if (latency - (*req)->nr_segments < 0) + break; + action = ELEVATOR_BACK_MERGE; + } else if ((*req)->sector - count == bh->b_rsector) { + if (starving) + break; + action = ELEVATOR_FRONT_MERGE; + } else { + continue; + } + q->elevator.sequence++; + return action; + } + return ELEVATOR_NO_MERGE; +} + +inline void elevator_default_dequeue(struct request *req) +{ + if (req->cmd == READ) + req->e->read_pendings--; + + req->e->nr_segments -= req->nr_segments; +} + +/* + * No request sorting, just add it to the back of the list + */ +void elevator_noop(struct request *req, elevator_t *elevator, + struct list_head *real_head, struct list_head *head, + int orig_latency) +{ + list_add_tail(&req->queue, real_head); +} + +/* + * See if we can find a request that is buffer can be coalesced with. + */ +int elevator_noop_merge(request_queue_t *q, struct request **req, + struct buffer_head *bh, int rw, + int *max_sectors, int *max_segments) +{ + struct list_head *entry, *head = &q->queue_head; + unsigned int count = bh->b_size >> 9; + + if (q->head_active && !q->plugged) + head = head->next; + + entry = head; + while ((entry = entry->prev) != head) { + *req = blkdev_entry_to_request(entry); + if ((*req)->sem) + continue; + if ((*req)->cmd != rw) + continue; + if ((*req)->nr_sectors + count > *max_sectors) + continue; + if ((*req)->rq_dev != bh->b_rdev) + continue; + if ((*req)->sector + (*req)->nr_sectors == bh->b_rsector) + return ELEVATOR_BACK_MERGE; + if ((*req)->sector - count == bh->b_rsector) + return ELEVATOR_FRONT_MERGE; + } + return ELEVATOR_NO_MERGE; +} + +/* + * The noop "elevator" does not do any accounting + */ +void elevator_noop_dequeue(struct request *req) {} + #ifdef ELEVATOR_DEBUG -void elevator_debug(request_queue_t * q, kdev_t dev) +void elevator_default_debug(request_queue_t * q, kdev_t dev) { int read_pendings = 0, nr_segments = 0; elevator_t * elevator = &q->elevator; struct list_head * entry = &q->queue_head; static int counter; + if (elevator->elevator_fn != elevator_default) + return; + if (counter++ % 100) return; - while ((entry = entry->prev) != &q->queue_head) - { + while ((entry = entry->prev) != &q->queue_head) { struct request * req; req = blkdev_entry_to_request(entry); @@ -81,16 +208,14 @@ nr_segments += req->nr_segments; } - if (read_pendings != elevator->read_pendings) - { + if (read_pendings != elevator->read_pendings) { printk(KERN_WARNING "%s: elevator read_pendings %d should be %d\n", kdevname(dev), elevator->read_pendings, read_pendings); elevator->read_pendings = read_pendings; } - if (nr_segments != elevator->nr_segments) - { + if (nr_segments != elevator->nr_segments) { printk(KERN_WARNING "%s: elevator nr_segments %d should be %d\n", kdevname(dev), elevator->nr_segments, @@ -102,52 +227,45 @@ int blkelvget_ioctl(elevator_t * elevator, blkelv_ioctl_arg_t * arg) { - int ret; blkelv_ioctl_arg_t output; output.queue_ID = elevator->queue_ID; output.read_latency = elevator->read_latency; output.write_latency = elevator->write_latency; output.max_bomb_segments = elevator->max_bomb_segments; + strcpy(output.elevator_name, elevator->elevator_name); - ret = -EFAULT; if (copy_to_user(arg, &output, sizeof(blkelv_ioctl_arg_t))) - goto out; - ret = 0; - out: - return ret; + return -EFAULT; + + return 0; } int blkelvset_ioctl(elevator_t * elevator, const blkelv_ioctl_arg_t * arg) { blkelv_ioctl_arg_t input; - int ret; - ret = -EFAULT; if (copy_from_user(&input, arg, sizeof(blkelv_ioctl_arg_t))) - goto out; + return -EFAULT; - ret = -EINVAL; if (input.read_latency < 0) - goto out; + return -EINVAL; if (input.write_latency < 0) - goto out; + return -EINVAL; if (input.max_bomb_segments <= 0) - goto out; + return -EINVAL; elevator->read_latency = input.read_latency; elevator->write_latency = input.write_latency; elevator->max_bomb_segments = input.max_bomb_segments; - ret = 0; - out: - return ret; + return 0; } -void elevator_init(elevator_t * elevator) +void elevator_init(elevator_t * elevator, elevator_t type) { static unsigned int queue_ID; - *elevator = ELEVATOR_DEFAULTS; + *elevator = type; elevator->queue_ID = queue_ID++; } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/block/ll_rw_blk.c Tue Jun 20 03:24:39 2000 +++ linux/drivers/block/ll_rw_blk.c Tue Jun 20 02:18:36 2000 @@ -4,6 +4,7 @@ * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1994, Karl Keyte: Added support for disk statistics * Elevator latency, (C) 2000 Andrea Arcangeli SuSE + * Queue request tables / lock, selectable elevator, Jens Axboe */ /* @@ -37,10 +38,9 @@ #endif /* - * The request-struct contains all necessary data - * to load a nr of sectors into memory + * For the allocated request tables */ -static struct request all_requests[NR_REQUEST]; +static kmem_cache_t *request_cachep; /* * The "disk" task queue is used to start the actual requests @@ -62,11 +62,6 @@ */ spinlock_t io_request_lock = SPIN_LOCK_UNLOCKED; -/* - * used to wait on when there are no free requests - */ -DECLARE_WAIT_QUEUE_HEAD(wait_for_request); - /* This specifies how many sectors to read ahead on the disk. */ int read_ahead[MAX_BLKDEV]; @@ -127,29 +122,53 @@ return max_sectors[MAJOR(dev)][MINOR(dev)]; } +static inline request_queue_t *__blk_get_queue(kdev_t dev) +{ + struct blk_dev_struct *bdev = blk_dev + MAJOR(dev); + + if (bdev->queue) + return bdev->queue(dev); + else + return &blk_dev[MAJOR(dev)].request_queue; +} + /* * NOTE: the device-specific queue() functions * have to be atomic! */ -request_queue_t * blk_get_queue (kdev_t dev) +request_queue_t *blk_get_queue(kdev_t dev) { - int major = MAJOR(dev); - struct blk_dev_struct *bdev = blk_dev + major; - unsigned long flags; request_queue_t *ret; + unsigned long flags; spin_lock_irqsave(&io_request_lock,flags); - if (bdev->queue) - ret = bdev->queue(dev); - else - ret = &blk_dev[major].request_queue; + ret = __blk_get_queue(dev); spin_unlock_irqrestore(&io_request_lock,flags); return ret; } +/* + * Hopefully the low level driver has finished any out standing requests + * first... + */ void blk_cleanup_queue(request_queue_t * q) { + struct list_head *entry; + struct request *rq; + + if (list_empty(&q->request_freelist)) + BUG(); + + entry = &q->request_freelist; + entry = entry->next; + do { + rq = list_entry(entry, struct request, table); + entry = entry->next; + list_del(&rq->table); + kmem_cache_free(request_cachep, rq); + } while (!list_empty(&q->request_freelist)); + memset(q, 0, sizeof(*q)); } @@ -222,7 +241,7 @@ * This is called with interrupts off and no requests on the queue. * (and with the request spinlock aquired) */ -static void generic_plug_device (request_queue_t *q, kdev_t dev) +static void generic_plug_device(request_queue_t *q, kdev_t dev) { #ifdef CONFIG_BLK_DEV_MD if (MAJOR(dev) == MD_MAJOR) { @@ -230,26 +249,51 @@ BUG(); } #endif - if (!list_empty(&q->queue_head)) + if (!list_empty(&q->queue_head) || q->plugged) return; q->plugged = 1; queue_task(&q->plug_tq, &tq_disk); } +static void blk_init_free_list(request_queue_t *q) +{ + struct request *rq; + int i; + + /* + * Divide requests in half between read and write. This used to + * be a 2/3 advantage for reads, but now reads can steal from + * the write free list. + */ + for (i = 0; i < QUEUE_NR_REQUESTS; i++) { + rq = kmem_cache_alloc(request_cachep, SLAB_KERNEL); + rq->rq_status = RQ_INACTIVE; + list_add(&rq->table, &q->request_freelist); + } + + q->queue_requests = 0; + init_waitqueue_head(&q->wait_for_request); + spin_lock_init(&q->request_lock); +} + void blk_init_queue(request_queue_t * q, request_fn_proc * rfn) { INIT_LIST_HEAD(&q->queue_head); - elevator_init(&q->elevator); + INIT_LIST_HEAD(&q->request_freelist); + elevator_init(&q->elevator, ELEVATOR_DEFAULT); + blk_init_free_list(q); q->request_fn = rfn; q->back_merge_fn = ll_back_merge_fn; q->front_merge_fn = ll_front_merge_fn; + q->hole_merge_fn = NULL; q->merge_requests_fn = ll_merge_requests_fn; q->make_request_fn = NULL; - q->plug_tq.sync = 0; - q->plug_tq.routine = &generic_unplug_device; - q->plug_tq.data = q; q->plugged = 0; + q->plug_tq.sync = 0; + q->plug_tq.routine = &generic_unplug_device; + q->plug_tq.data = q; + /* * These booleans describe the queue properties. We set the * default (and most common) values here. Other drivers can @@ -263,89 +307,83 @@ /* * remove the plug and let it rip.. */ -void generic_unplug_device(void * data) +static inline void __generic_unplug_device(request_queue_t *q) { - request_queue_t * q = (request_queue_t *) data; - unsigned long flags; - - spin_lock_irqsave(&io_request_lock,flags); if (q->plugged) { q->plugged = 0; if (!list_empty(&q->queue_head)) - (q->request_fn)(q); + q->request_fn(q); } - spin_unlock_irqrestore(&io_request_lock,flags); } +void generic_unplug_device(void *data) +{ + request_queue_t *q = (request_queue_t *) data; + unsigned long flags; + + spin_lock_irqsave(&io_request_lock, flags); + __generic_unplug_device(q); + spin_unlock_irqrestore(&io_request_lock, flags); +} + +#define blkdev_free_rq(list) list_entry((list)->next, struct request, table); /* - * look for a free request in the first N entries. - * NOTE: interrupts must be disabled on the way in (on SMP the request queue - * spinlock has to be aquired), and will still be disabled on the way out. + * Get a free request. io_request_lock must be held and interrupts + * disabled on the way in. */ -static inline struct request * get_request(int n, kdev_t dev) +static inline struct request *get_request(request_queue_t *q, int rw) { - static struct request *prev_found = NULL, *prev_limit = NULL; - register struct request *req, *limit; + struct request *rq = NULL; - if (n <= 0) - panic("get_request(%d): impossible!\n", n); + if (!list_empty(&q->request_freelist)) { - limit = all_requests + n; - if (limit != prev_limit) { - prev_limit = limit; - prev_found = all_requests; - } - req = prev_found; - for (;;) { - req = ((req > all_requests) ? req : limit) - 1; - if (req->rq_status == RQ_INACTIVE) - break; - if (req == prev_found) + if ((q->queue_requests > QUEUE_WRITES_MAX) && (rw == WRITE)) return NULL; + + rq = blkdev_free_rq(&q->request_freelist); + list_del(&rq->table); + rq->rq_status = RQ_ACTIVE; + rq->special = NULL; + rq->q = q; + q->queue_requests++; } - prev_found = req; - req->rq_status = RQ_ACTIVE; - req->rq_dev = dev; - req->special = NULL; - return req; + return rq; } /* - * wait until a free request in the first N entries is available. + * No available requests for this queue, unplug the device. */ -static struct request * __get_request_wait(int n, kdev_t dev) +static struct request *__get_request_wait(request_queue_t *q, int rw) { - register struct request *req; + register struct request *rq; DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - add_wait_queue_exclusive(&wait_for_request, &wait); + add_wait_queue_exclusive(&q->wait_for_request, &wait); for (;;) { - __set_current_state(TASK_UNINTERRUPTIBLE|TASK_EXCLUSIVE); - spin_lock_irqsave(&io_request_lock,flags); - req = get_request(n, dev); - spin_unlock_irqrestore(&io_request_lock,flags); - if (req) + __set_current_state(TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + spin_lock_irq(&io_request_lock); + rq = get_request(q, rw); + spin_unlock_irq(&io_request_lock); + if (rq) break; - run_task_queue(&tq_disk); + generic_unplug_device(q); schedule(); } - remove_wait_queue(&wait_for_request, &wait); + remove_wait_queue(&q->wait_for_request, &wait); current->state = TASK_RUNNING; - return req; + return rq; } -static inline struct request * get_request_wait(int n, kdev_t dev) +inline struct request *get_request_wait(request_queue_t *q, int rw) { - register struct request *req; - unsigned long flags; + register struct request *rq; - spin_lock_irqsave(&io_request_lock,flags); - req = get_request(n, dev); - spin_unlock_irqrestore(&io_request_lock,flags); - if (req) - return req; - return __get_request_wait(n, dev); + spin_lock_irq(&io_request_lock); + rq = get_request(q, rw); + spin_unlock_irq(&io_request_lock); + if (rq) + return rq; + return __get_request_wait(q, rw); } /* RO fail safe mechanism */ @@ -405,36 +443,45 @@ */ static inline void add_request(request_queue_t * q, struct request * req, - struct list_head * head, int latency) + struct list_head *head, int lat) { int major; drive_stat_acct(req->rq_dev, req->cmd, req->nr_sectors, 1); - - elevator_account_request(&q->elevator, req); - if (list_empty(head)) { - req->elevator_sequence = elevator_sequence(&q->elevator, latency); - list_add(&req->queue, &q->queue_head); - return; - } - q->elevator.elevator_fn(req, &q->elevator, &q->queue_head, head, latency); - + elevator_account_request(req); /* + * let selected elevator insert the request + */ + q->elevator.elevator_fn(req, &q->elevator, &q->queue_head, head, lat); + + /* * FIXME(eric) I don't understand why there is a need for this * special case code. It clearly doesn't fit any more with * the new queueing architecture, and it got added in 2.3.10. * I am leaving this in here until I hear back from the COMPAQ * people. - */ + */ major = MAJOR(req->rq_dev); if (major >= COMPAQ_SMART2_MAJOR+0 && major <= COMPAQ_SMART2_MAJOR+7) - { - (q->request_fn)(q); - } - + q->request_fn(q); if (major >= DAC960_MAJOR+0 && major <= DAC960_MAJOR+7) - { - (q->request_fn)(q); + q->request_fn(q); +} + +/* + * Must be called with io_request_lock held and interrupts disabled + */ +void inline blkdev_release_request(struct request *req) +{ + req->rq_status = RQ_INACTIVE; + + /* + * Request may not have originated from ll_rw_blk + */ + if (req->q) { + list_add(&req->table, &req->q->request_freelist); + req->q->queue_requests--; + wake_up(&req->q->wait_for_request); } } @@ -449,10 +496,12 @@ struct request *next; next = blkdev_next_request(req); - if (req->sector + req->nr_sectors != next->sector) + if (req->sector + req->nr_sectors != next->sector) { return; - if (req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors > max_sectors || next->sem) + } + if (req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors > max_sectors || next->sem) { return; + } /* * If we are not allowed to merge these requests, then * return. If we are allowed to merge, then the count @@ -462,13 +511,12 @@ if(!(q->merge_requests_fn)(q, req, next, max_segments)) return; - elevator_merge_requests(&q->elevator, req, next); + elevator_merge_requests(req, next); req->bhtail->b_reqnext = next->bh; req->bhtail = next->bhtail; req->nr_sectors = req->hard_nr_sectors += next->hard_nr_sectors; - next->rq_status = RQ_INACTIVE; list_del(&next->queue); - wake_up (&wait_for_request); + blkdev_release_request(next); } static inline void attempt_back_merge(request_queue_t * q, @@ -496,18 +544,16 @@ } static inline void __make_request(request_queue_t * q, int rw, - struct buffer_head * bh) + struct buffer_head * bh) { int major = MAJOR(bh->b_rdev); unsigned int sector, count; int max_segments = MAX_SEGMENTS; - struct request * req; - int rw_ahead, max_req, max_sectors; - unsigned long flags; - - int orig_latency, latency, starving, sequence; - struct list_head * entry, * head = &q->queue_head; - elevator_t * elevator; + struct request * req = NULL; + int rw_ahead, max_sectors, el_ret; + struct list_head *head = &q->queue_head; + int latency; + elevator_t *elevator = &q->elevator; count = bh->b_size >> 9; sector = bh->b_rsector; @@ -541,7 +587,6 @@ if (buffer_uptodate(bh)) /* Hmmph! Already have it */ goto end_io; kstat.pgpgin++; - max_req = NR_REQUEST; /* reads take precedence */ break; case WRITERAW: rw = WRITE; @@ -558,7 +603,6 @@ * requests are only for reads. */ kstat.pgpgout++; - max_req = (NR_REQUEST * 2) / 3; break; default: BUG(); @@ -583,153 +627,87 @@ /* look for a free request. */ /* - * Loop uses two requests, 1 for loop and 1 for the real device. - * Cut max_req in half to avoid running out and deadlocking. - */ - if ((major == LOOP_MAJOR) || (major == NBD_MAJOR)) - max_req >>= 1; - - /* * Try to coalesce the new request with old requests */ max_sectors = get_max_sectors(bh->b_rdev); - elevator = &q->elevator; - orig_latency = elevator_request_latency(elevator, rw); + latency = elevator_request_latency(elevator, rw); /* * Now we acquire the request spinlock, we have to be mega careful * not to schedule or do something nonatomic */ - spin_lock_irqsave(&io_request_lock,flags); - elevator_debug(q, bh->b_rdev); + spin_lock_irq(&io_request_lock); + elevator_default_debug(q, bh->b_rdev); if (list_empty(head)) { q->plug_device_fn(q, bh->b_rdev); /* is atomic */ goto get_rq; } - /* avoid write-bombs to not hurt iteractiveness of reads */ - if (rw != READ && elevator->read_pendings) - max_segments = elevator->max_bomb_segments; - - sequence = elevator->sequence; - latency = orig_latency - elevator->nr_segments; - starving = 0; - entry = head; - - /* - * The scsi disk and cdrom drivers completely remove the request - * from the queue when they start processing an entry. For this - * reason it is safe to continue to add links to the top entry - * for those devices. - * - * All other drivers need to jump over the first entry, as that - * entry may be busy being processed and we thus can't change - * it. - */ - if (q->head_active && !q->plugged) - head = head->next; + el_ret = elevator->elevator_merge_fn(q, &req, bh, rw, &max_sectors, &max_segments); + switch (el_ret) { - while ((entry = entry->prev) != head && !starving) { - req = blkdev_entry_to_request(entry); - if (!req->q) - break; - latency += req->nr_segments; - if (elevator_sequence_before(req->elevator_sequence, sequence)) - starving = 1; - if (latency < 0) - continue; - - if (req->sem) - continue; - if (req->cmd != rw) - continue; - if (req->nr_sectors + count > max_sectors) - continue; - if (req->rq_dev != bh->b_rdev) - continue; - /* Can we add it to the end of this request? */ - if (req->sector + req->nr_sectors == sector) { - if (latency - req->nr_segments < 0) - break; - /* - * The merge_fn is a more advanced way - * of accomplishing the same task. Instead - * of applying a fixed limit of some sort - * we instead define a function which can - * determine whether or not it is safe to - * merge the request or not. - * - * See if this queue has rules that - * may suggest that we shouldn't merge - * this - */ - if(!(q->back_merge_fn)(q, req, bh, max_segments)) + case ELEVATOR_BACK_MERGE: + if (!q->back_merge_fn(q, req, bh, max_segments)) break; req->bhtail->b_reqnext = bh; req->bhtail = bh; - req->nr_sectors = req->hard_nr_sectors += count; + req->nr_sectors = req->hard_nr_sectors += count; + req->e = elevator; drive_stat_acct(req->rq_dev, req->cmd, count, 0); - - elevator_merge_after(elevator, req, latency); - - /* Can we now merge this req with the next? */ attempt_back_merge(q, req, max_sectors, max_segments); - /* or to the beginning? */ - } else if (req->sector - count == sector) { - if (starving) - break; - /* - * The merge_fn is a more advanced way - * of accomplishing the same task. Instead - * of applying a fixed limit of some sort - * we instead define a function which can - * determine whether or not it is safe to - * merge the request or not. - * - * See if this queue has rules that - * may suggest that we shouldn't merge - * this - */ - if(!(q->front_merge_fn)(q, req, bh, max_segments)) + goto out; + + case ELEVATOR_FRONT_MERGE: + if (!q->front_merge_fn(q, req, bh, max_segments)) break; - bh->b_reqnext = req->bh; - req->bh = bh; - req->buffer = bh->b_data; - req->current_nr_sectors = count; - req->sector = req->hard_sector = sector; - req->nr_sectors = req->hard_nr_sectors += count; + bh->b_reqnext = req->bh; + req->bh = bh; + req->buffer = bh->b_data; + req->current_nr_sectors = count; + req->sector = req->hard_sector = sector; + req->nr_sectors = req->hard_nr_sectors += count; + req->e = elevator; drive_stat_acct(req->rq_dev, req->cmd, count, 0); - - elevator_merge_before(elevator, req, latency); - attempt_front_merge(q, head, req, max_sectors, max_segments); - } else - continue; + goto out; - q->elevator.sequence++; - spin_unlock_irqrestore(&io_request_lock,flags); - return; + /* + * hole merge is only part of pktcdvd for now, and it has + * already done the merging at this point + */ + case ELEVATOR_HOLE_MERGE: + goto out; + /* + * elevator says don't/can't merge. get new request + */ + case ELEVATOR_NO_MERGE: + break; + default: + printk("elevator returned crap (%d)\n", el_ret); + BUG(); } - -/* find an unused request. */ -get_rq: - req = get_request(max_req, bh->b_rdev); - + /* - * if no request available: if rw_ahead, forget it, - * otherwise try again blocking.. + * Grab a free request from the freelist. Read first try their + * own queue - if that is empty, we steal from the write list. + * Writes must block if the write list is empty, and read aheads + * are not crucial. */ - if (!req) { - spin_unlock_irqrestore(&io_request_lock,flags); +get_rq: + if ((req = get_request(q, rw)) == NULL) { + spin_unlock_irq(&io_request_lock); if (rw_ahead) goto end_io; - req = __get_request_wait(max_req, bh->b_rdev); - spin_lock_irqsave(&io_request_lock,flags); - /* revalidate elevator */ + req = __get_request_wait(q, rw); + spin_lock_irq(&io_request_lock); + + /* + * revalidate elevator, queue request_lock was dropped + */ head = &q->queue_head; if (q->head_active && !q->plugged) head = head->next; @@ -747,19 +725,18 @@ req->sem = NULL; req->bh = bh; req->bhtail = bh; - req->q = q; - add_request(q, req, head, orig_latency); - - spin_unlock_irqrestore(&io_request_lock, flags); + req->rq_dev = bh->b_rdev; + req->e = elevator; + add_request(q, req, head, latency); +out: + spin_unlock_irq(&io_request_lock); return; - end_io: bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state)); } int generic_make_request (request_queue_t *q, int rw, struct buffer_head * bh) { - unsigned long flags; int ret; /* @@ -767,7 +744,6 @@ * still free to implement/resolve their own stacking * by explicitly returning 0) */ - while (q->make_request_fn) { ret = q->make_request_fn(q, rw, bh); if (ret > 0) { @@ -781,10 +757,10 @@ * the IO request? (normal case) */ __make_request(q, rw, bh); - spin_lock_irqsave(&io_request_lock,flags); + spin_lock_irq(&io_request_lock); if (q && !q->plugged) (q->request_fn)(q); - spin_unlock_irqrestore(&io_request_lock,flags); + spin_unlock_irq(&io_request_lock); return 0; } @@ -911,8 +887,9 @@ req->current_nr_sectors = bh->b_size >> 9; if (req->nr_sectors < req->current_nr_sectors) { + printk("end_request: buffer-list destroyed (%ld < %ld\n", req->nr_sectors, req->current_nr_sectors); + printk("sector %ld\n", bh->b_rsector); req->nr_sectors = req->current_nr_sectors; - printk("end_request: buffer-list destroyed\n"); } req->buffer = bh->b_data; return 1; @@ -923,28 +900,27 @@ void end_that_request_last(struct request *req) { - if (req->q) + if (req->e) { + printk("end_that_request_last called with non-dequeued req\n"); BUG(); + } if (req->sem != NULL) up(req->sem); - req->rq_status = RQ_INACTIVE; - wake_up(&wait_for_request); + + blkdev_release_request(req); } int __init blk_dev_init(void) { - struct request * req; struct blk_dev_struct *dev; - for (dev = blk_dev + MAX_BLKDEV; dev-- != blk_dev;) { + request_cachep = kmem_cache_create("blkdev_requests", + sizeof(struct request), + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + + for (dev = blk_dev + MAX_BLKDEV; dev-- != blk_dev;) dev->queue = NULL; - blk_init_queue(&dev->request_queue, NULL); - } - req = all_requests + NR_REQUEST; - while (--req >= all_requests) { - req->rq_status = RQ_INACTIVE; - } memset(ro_bits,0,sizeof(ro_bits)); memset(max_readahead, 0, sizeof(max_readahead)); memset(max_sectors, 0, sizeof(max_sectors)); @@ -1070,3 +1046,5 @@ EXPORT_SYMBOL(blk_queue_pluggable); EXPORT_SYMBOL(blk_queue_make_request); EXPORT_SYMBOL(generic_make_request); +EXPORT_SYMBOL(blkdev_release_request); +EXPORT_SYMBOL(get_request_wait); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/block/pktcdvd.c linux/drivers/block/pktcdvd.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/block/pktcdvd.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/block/pktcdvd.c Tue Jun 20 03:21:48 2000 @@ -0,0 +1,1753 @@ +/* + * Copyright (C) 2000 Jens Axboe + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and + * DVD-RW devices. + * + * + * TODO: (circa order of when I will fix it) + * - Fix locking, don't rely on io_request_lock. pd->lock is in place, + * just not fully used. + * - Only able to write on CD-RW media right now. + * - Generic interface for UDF to submit large packets for variable length + * packet writing. + * - (in correlation with above) interface for UDF <-> packet to negotiate + * a new location when a write fails. + * - handle OPC, especially for -RW media + * - /proc/driver/pktcdvd with info + * + * Known "issues": + * - Only use with regular file system + * + * ------------------------------------------------------------------------ + * + * 0.0.2a (19/06/2000) + * - add kpacketd kernel thread to handle actual data gathering + * - pd->pkt_dev is now real device, not just minor + * - add support for super_operations block_empty fn, to query fs for + * unused blocks that don't need reading + * - "cache" blocks that are contained in the UDF file/dir packet + * - rewrite pkt_gather_data to a one-step solution + * - add private pktcdvd elevator + * - shutdown write access to device upon write failure + * - fix off-by-one bug in capacity + * - setup sourceforge project (packet-cd.sourceforge.net) + * - add more blk ioctls to pkt_ioctl + * - set inactive request queue head + * - change panic calls to BUG, better with kdb + * - have pkt_gather_data check correct block size and kill rq if wrong + * - rework locking + * - introduce per-pd queues, simplifies pkt_request + * - store pd in queuedata + * + *************************************************************************/ + +#define VERSION_CODE "v0.0.2a 19/06/2000 Jens Axboe (axboe@suse.de)" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern void end_buffer_io_async(struct buffer_head *bh, int uptodate); + +/* + * This is broken beyond belief, but it works for freshly generated + * CD-RW disks in Linux - Ben and I need a serious chat about this ;) + */ +#define PACKET_UDF_DIR 1408 + +/* + * 1 for normal debug messages, 2 is very verbose. 0 to turn it off. + */ +#define PACKET_DEBUG 1 + +#define MAX_WRITERS 8 + +/* + * 64 * 1024 sectors + */ +#define PACKET_MAX_SIZE 64 + +#define NEXT_BH(bh, nbh) (((bh)->b_rsector + ((bh)->b_size >> 9)) == (nbh)->b_rsector) + +#define BH_IN_ORDER(b1, b2) ((b1)->b_rsector < (b2)->b_rsector) + +#define CONTIG_BH(b1, b2) ((b1)->b_data + (b1)->b_size == (b2)->b_data) + +#define ZONE(sector, span, offset) \ + (((sector) + (offset)) - (((sector) + (offset)) % (span))) + +#define ASSERT(expr) \ + if (!(expr)) { \ + printk("assert failed %s,%s at %d\n", \ + #expr, __FUNCTION__, __LINE__); \ + } + +#if PACKET_DEBUG +#define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args) +#else +#define DPRINTK(fmt, args...) +#endif + +#if PACKET_DEBUG > 1 +#define VPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args) +#else +#define VPRINTK(fmt, args...) +#endif + +static int *pkt_sizes; +static int *pkt_blksize; +static int *pkt_readahead; +static struct pktcdvd_device *pkt_devs; +static pid_t kpacketd_pid; +static int kpacketd_running = 0; +static DECLARE_WAIT_QUEUE_HEAD(kpacketd_wqueue); + +/* + * a bit of a kludge, but we want to be able to pass both real and packet + * dev and get the right one. + */ +static inline struct pktcdvd_device *pkt_find_dev(kdev_t dev) +{ + int i; + + for (i = 0; i < MAX_WRITERS; i++) { + if (pkt_devs[i].dev == dev || pkt_devs[i].pkt_dev == dev) + return &pkt_devs[i]; + } + return NULL; +} + +static void pkt_recheck_segments(struct request *rq, char *name) +{ + struct buffer_head *bh = rq->bh; + int nr_segments = 1, sectors = rq->bh->b_size >> 9; + + while (bh->b_reqnext) { + if (!CONTIG_BH(bh, bh->b_reqnext)) + nr_segments++; + bh = bh->b_reqnext; + sectors += bh->b_size >> 9; + } + + /* + * this will happen + */ + if (nr_segments != rq->nr_segments) + rq->nr_segments = nr_segments; + + if (sectors != rq->nr_sectors) { + printk("from %s sector mismatch %u != %lu\n", name, sectors, rq->nr_sectors); + BUG(); + } + + /* + * paranoia + */ + if (rq->nr_segments > rq->nr_sectors) { + printk("corrupted request nr_segments > nr_sectors\n"); + BUG(); + } +} + +/* + * The following three functions are the plugins to the ll_rw_blk + * layer and decides whether a given request / buffer head can be + * merged. We differ in a couple of ways from "normal" block + * devices: + * + * - don't merge when the buffer / request crosses a packet block + * boundary + * - merge buffer head even though it can't be added directly to the + * front or back of the list. this gives us better performance, since + * what would otherwise require multiple requests can now be handled + * in one. + * - we only care about write merging, reads use device original defaults. + * + * The device original merge_ functions are stored in the packet device + * queue (pd->q) + * + */ +static int pkt_front_merge_fn(request_queue_t *q, struct request *rq, + struct buffer_head *bh, int max_segments) +{ + struct pktcdvd_device *pd = pkt_find_dev(rq->rq_dev); + int span = pd->settings.size; + + /* + * dev might not be setup yet, so check span + */ + if (span) { + if (ZONE(rq->sector, span, pd->offset) != + ZONE(bh->b_rsector, span, pd->offset)) + return ELEVATOR_NO_MERGE; + } + + return pd->front_merge_fn(q, rq, bh, max_segments); +} + +static int pkt_back_merge_fn(request_queue_t *q, struct request *rq, + struct buffer_head *bh, int max_segments) +{ + struct pktcdvd_device *pd = pkt_find_dev(rq->rq_dev); + int span = pd->settings.size; + + /* + * dev might not be setup yet, so check span + */ + if (span) { + if (ZONE(rq->sector, span, pd->offset) != + ZONE(bh->b_rsector, span, pd->offset)) + return ELEVATOR_NO_MERGE; + } + + return pd->back_merge_fn(q, rq, bh, max_segments); +} + +/* + * rules similar to above + */ +static int pkt_merge_requests_fn(request_queue_t *q, struct request *rq, + struct request *next, int max_segments) +{ + struct pktcdvd_device *pd = pkt_find_dev(rq->rq_dev); + int span = pd->settings.size; + + if (span) { + if (ZONE(rq->sector, span, pd->offset) != + ZONE(next->sector + next->nr_sectors, span, pd->offset)) + return 0; + } + + return pd->merge_requests_fn(q, rq, next, max_segments); +} + +/* + * The logic here is try queue default merge first and if is says ok, + * fine. If not, we find the appropriate place to insert this buffer + * head in the list, so that they are ordered before we receive the + * request. + */ +static int pkt_hole_merge_fn(request_queue_t *q, struct request *rq, + struct buffer_head *bh, int max_segments) +{ + struct pktcdvd_device *pd = pkt_find_dev(rq->rq_dev); + int span = pd->settings.size; + struct buffer_head *tmp; + + if (span) { + if (ZONE(rq->sector, span, pd->offset) != + ZONE(bh->b_rsector, span, pd->offset)) + return ELEVATOR_NO_MERGE; + } + + /* holes only supported for writing */ + if (rq->cmd == READ) + return ELEVATOR_NO_MERGE; + + if (rq->nr_segments >= max_segments) + return ELEVATOR_NO_MERGE; + + rq->nr_segments++; + + /* stuff in front? */ + if (bh->b_rsector < rq->sector) { + bh->b_reqnext = rq->bh; + rq->bh = bh; + rq->sector = rq->hard_sector = bh->b_rsector; + rq->current_nr_sectors = bh->b_size >> 9; + goto out; + } + + /* stuff in back? */ + if (bh->b_rsector > rq->bhtail->b_rsector) { + rq->bhtail->b_reqnext = bh; + rq->bhtail = bh; + goto out; + } + + /* find sweet spot */ + for (tmp = rq->bh; tmp->b_reqnext; tmp = tmp->b_reqnext) + if (BH_IN_ORDER(tmp, bh) && BH_IN_ORDER(bh, tmp->b_reqnext)) + break; + + bh->b_reqnext = tmp->b_reqnext; + tmp->b_reqnext = bh; +out: + rq->buffer = rq->bh->b_data; + rq->nr_sectors += (bh->b_size >> 9); + rq->hard_nr_sectors = rq->nr_sectors; + pkt_recheck_segments(rq, "pkt_hole_merge"); + return ELEVATOR_HOLE_MERGE; +} + +static request_queue_t *__pkt_get_queue(kdev_t dev) +{ + struct blk_dev_struct *bdev = blk_dev + MAJOR(dev); + + if (bdev->queue) + return bdev->queue(dev); + else + return &blk_dev[MAJOR(dev)].request_queue; +} + +static request_queue_t *pkt_get_queue(kdev_t dev) +{ + unsigned long flags; + request_queue_t *q; + spin_lock_irqsave(&io_request_lock, flags); + q = __pkt_get_queue(dev); + spin_unlock_irqrestore(&io_request_lock, flags); + return q; +} + +static request_queue_t *pkt_my_queue(kdev_t dev) +{ + struct pktcdvd_device *pd = pkt_find_dev(dev); + return &pd->queue; +} + +/* + * Remap current request to a new location. Needs to "discuss" these + * things with UDF. + */ +#if 0 +static void pkt_remap_write(struct buffer_head *bh) +{ + request_queue_t *q; + struct request *rq; + unsigned long flags; + + spin_lock_irqsave(&io_request_lock, flags); + + /* FIXME: bh should probably be marked BH_Protected so that + * they are kept in the cache until a write is ended. Then + * we can quickly get ahold of them again and resubmit a + * write to a new location + */ + q = __pkt_get_queue(bh->b_dev); + rq = blkdev_entry_prev_request(&q->queue_head); + if (rq) { + printk("pktcdvd: auch, can't remap request\n"); +#if 0 + spin_unlock_irqrestore(&io_request_lock, flags); + return; +#endif + } + mark_buffer_uptodate(bh, 0); + unlock_buffer(bh); + if (bh->b_dev_id == pkt_end_io_write) { + bh->b_dev_id = NULL; + brelse(bh); + } + + if (!list_empty(&q->queue_head)) { + end_that_request_last(q->current_request); + q->current_request = q->current_request->next; + } + spin_unlock_irqrestore(&io_request_lock, flags); +} +#endif + +/* + * we use this as our default b_end_io handler, since we need to take + * the entire request off the list if just on of the clusters fail. + * later one this should also talk to UDF about relocating blocks -- for + * now we just drop the rq entirely. when doing the relocating we must also + * lock the bh down to ensure that we can easily reconstruct the write should + * it fail. + */ +static void pkt_end_io_write(struct buffer_head *bh, int uptodate) +{ + struct pktcdvd_device *pd = pkt_find_dev(bh->b_dev); + + if (!uptodate) { +#if 0 + pkt_remap_write(bh); +#else + /* + * for now, just shutdown write access + */ + set_bit(PACKET_READONLY, &pd->flags); + return; +#endif + } + + mark_buffer_uptodate(bh, uptodate); + unlock_buffer(bh); + if (bh->b_dev_id == pkt_end_io_write) { + bh->b_dev_id = NULL; + brelse(bh); + } + pd->stats.bh_e++; +} + +static void pkt_show_stats(kdev_t dev) +{ + struct pktcdvd_device *pd = pkt_find_dev(dev); + printk("BH: started (%lu) ended (%lu)\n", pd->stats.bh_s, pd->stats.bh_e); +} + +/* + * replace b_end_io with our own, so that we can keep bhs in cache for + * the duration of the entire write. + */ +static void pkt_init_bh(struct request *rq) +{ + struct buffer_head *bh; + unsigned cnt = 0; + + bh = rq->bh; + while (bh) { + /* FIXME */ + if (bh->b_end_io == end_buffer_io_async) { + printk("woops, page cache...\n"); + BUG(); + } + bh->b_end_io = pkt_end_io_write; + if (!buffer_uptodate(bh) && bh->b_dev_id != pkt_end_io_write) + printk("that's why?!\n"); + bh = bh->b_reqnext; + cnt += rq->current_nr_sectors; + } + + if (cnt != rq->nr_sectors) { + pkt_show_stats(rq->rq_dev); + show_buffers(); + printk("botched request %u (%lu)\n", cnt, rq->nr_sectors); + BUG(); + } +} + +/* + * really crude stats for now... + */ +static void pkt_add_stats(struct pktcdvd_device *pd, int read, int written, + int bs) +{ + pd->stats.bh_s += (written / bs); + pd->stats.blocks_written += written; + pd->stats.blocks_read += read; +} + +static void pkt_show_bhstring(struct buffer_head *bh) +{ + int i = 0; + + while (bh && i < 128) { + DPRINTK("bh at %lu\n", bh->b_rsector); + bh = bh->b_reqnext; + i++; + } + DPRINTK("%u bhs in all\n", i); +} + +/* + * does request span two packets? 0 == yes, 1 == no + */ +static inline int pkt_same_zone(struct pktcdvd_device *pd, struct request *rq) +{ + int span = pd->settings.size; + + if (ZONE(rq->sector, span, pd->offset) != + ZONE(rq->sector + rq->nr_sectors - 1, span, pd->offset)) + return 0; + + return 1; +} + +/* + * basically just does a ll_rw_block for the bhs given to use, but we + * don't return until we have them. + * + * Locks: pd->pkt_sem + */ +static void pkt_read_bhlist(struct pktcdvd_device *pd, + struct buffer_head *bhlist[], int count) +{ + struct super_block *sb; + struct super_operations *sop = NULL; + int i; + +#if 0 + if ((sb = get_super(pd->pkt_dev)) == NULL) + printk("get super failed for %s\n", kdevname(pd->pkt_dev)); + else + sop = sb->s_op; +#else + sb = NULL; + sop = NULL; +#endif + + for (i = 0; i < count; i++) { + struct buffer_head *bh = bhlist[i]; + unsigned long packet = 0, blocknr = bh->b_rsector >> 2; + + if (sop && sop->block_empty) { + printk("has block_empty\n"); + if (!sop->block_empty(sb, blocknr, &packet)) { + printk("block %lu empty\n", bh->b_rsector); + memset(bh->b_data, 0, bh->b_size); + mark_buffer_uptodate(bh, 1); + mark_buffer_clean(bh); + continue; + } else { + printk("block %lu NOT empty\n", bh->b_rsector); + } + } + + ll_rw_block(READ, 1, &bh); + if (!buffer_uptodate(bh)) { + VPRINTK("waiting on buffer %lu\n", bh->b_rsector); + lock_buffer(bh); + } +#if 0 + if (bh->b_rsector - PACKET_UDF_DIR < pd->settings.size) { + printk("marked block part of dir/file count %lu\n", bh->b_rsector); + mark_buffer_protected(bh); + } +#endif + VPRINTK("got buffer\n"); + } +} + +void inline pkt_kill_request(struct request *rq, int uptodate, char *name) +{ + spin_lock_irq(&io_request_lock); + while (end_that_request_first(rq, uptodate, name)) + ; + end_that_request_last(rq); + spin_unlock_irq(&io_request_lock); +} + +/* + * fill in the holes of a request + * + * Returns: 1 (failure), 0 (success) + * + * Locks: pd->pkt_sem + */ +static int pkt_gather_data(struct pktcdvd_device *pd, struct request *rq) +{ + unsigned long start_s, end_s, sector; + struct buffer_head *bh; + unsigned int sectors; + + /* + * all calculations are done with 512 byte sectors + */ + sectors = pd->settings.size - rq->nr_sectors; + start_s = rq->sector - (rq->sector % pd->settings.size); + end_s = start_s + pd->settings.size; + + VPRINTK("need %d sectors for %s\n", sectors, kdevname(pd->dev)); + VPRINTK("from %lu to %lu (%lu - %lu)\n", start_s, end_s, rq->bh->b_rsector, rq->bhtail->b_rsector + rq->current_nr_sectors); + +#if PACKET_DEBUG > 2 + pkt_show_bhstring(rq->bh); +#endif + + if (blksize_size[MAJOR(pd->dev)]) { + if (rq->bh->b_size != blksize_size[MAJOR(pd->dev)][MINOR(pd->dev)]) { + printk("pktcdvd: wrong (%u) block size\n", rq->bh->b_size); + pkt_kill_request(rq, 0, pd->name); + return 1; + } + } + + /* + * get remaining blocks + */ + bh = rq->bh; + for (sector = start_s; sector < end_s; sector += (bh->b_size >> 9)) { + struct buffer_head *foo_bh; + + if (sector < bh->b_rsector) + goto new; + + if (sector == bh->b_rsector) + continue; + + if (bh->b_reqnext && NEXT_BH(bh, bh->b_reqnext)) { + bh = bh->b_reqnext; + continue; + } + + new: + foo_bh = getblk(pd->dev, sector / (bh->b_size >> 9),bh->b_size); + foo_bh->b_dev_id = pkt_end_io_write; + pkt_read_bhlist(pd, &foo_bh, 1); + VPRINTK("got block %lu\n", foo_bh->b_rsector); + if (sector < bh->b_rsector) { + foo_bh->b_reqnext = bh; + rq->bh = foo_bh; + } else { + foo_bh->b_reqnext = bh->b_reqnext; + bh->b_reqnext = foo_bh; + } + bh = foo_bh; + rq->nr_sectors += (bh->b_size >> 9); + if (bh->b_rsector > rq->bhtail->b_rsector) + rq->bhtail = bh; + } + +#if PACKET_DEBUG > 2 + pkt_show_bhstring(rq->bh); +#endif + + rq->bhtail->b_reqnext = NULL; + rq->buffer = rq->bh->b_data; + rq->current_nr_sectors = rq->bh->b_size >> 9; + rq->hard_nr_sectors = rq->nr_sectors; + rq->sector = rq->hard_sector = start_s; + rq->cmd = WRITE_PACKET; + + VPRINTK("unlocked last %lu\n", rq->bhtail->b_rsector); + pkt_recheck_segments(rq, "pkt_gather_data"); + pkt_init_bh(rq); + pkt_add_stats(pd, sectors, rq->nr_sectors, rq->current_nr_sectors); + + /* sanity check */ + if (rq->nr_sectors != pd->settings.size) { + pkt_show_bhstring(rq->bh); + printk("pktcdvd: request mismatch %lu (should be %u)\n", + rq->nr_sectors, pd->settings.size); + BUG(); + } + return 0; +} + +/* + * Queue request at the end of the queue list + * + * FIXME: the get_request_wait hack needs to go. problem is, we can't + * just move a request from one queue to another without getting into + * problems at request release time... + * + * Locks: pd->pkt_sem + */ +inline struct request *get_request_wait(request_queue_t *q, int rw); + +static inline void pkt_queue_request(request_queue_t *q, struct request *rq) +{ + struct request *tmp = rq; + +#if PACKET_DEBUG + pkt_recheck_segments(rq, "pkt_queue_request"); +#endif + + tmp = get_request_wait(q, rq->cmd); + memcpy(tmp, rq, sizeof(struct request)); + tmp->q = q; + + spin_lock_irq(&io_request_lock); + blkdev_release_request(rq); + q->elevator.nr_segments += tmp->nr_segments; + + if (tmp->cmd == READ) + tmp->q->elevator.read_pendings++; + + if (list_empty(&q->queue_head)) + q->plug_device_fn(q, rq->rq_dev); + + list_add_tail(&tmp->queue, &q->queue_head); + spin_unlock_irq(&io_request_lock); +} + +/* + * Locks: pd->pkt_sem + */ +static void pkt_do_request(struct pktcdvd_device *pd, struct request *rq, + request_queue_t *pdq) +{ + /* + * perfect match. the merge_* functions have already made sure that + * a request doesn't cross a packet boundary, so if the sector + * count matches it's good. + */ + if (rq->nr_sectors == pd->settings.size) { + rq->cmd = WRITE_PACKET; + pkt_init_bh(rq); + pkt_queue_request(pdq, rq); + return; + } + + /* paranoia... */ + if (rq->nr_sectors > pd->settings.size) { + printk("pktcdvd: request too big! BUG! %lu\n", rq->nr_sectors); + BUG(); + } + + /* + * we get this far -- this means that data is lacking before we + * can submit a packet write request. + */ + if (pkt_gather_data(pd, rq)) { + printk("pktcdvd: %s failed data gather, rq killed\n", pd->name); + return; + } + pkt_queue_request(pdq, rq); +} + +/* + * handle the requests that got queued for this writer + * + * Locks: pd->pkt_sem + * + * Notes: pd->pkt_queue is currently protected by the pd->pkt_sem + * semaphore. + */ +static void pkt_handle_queue(struct pktcdvd_device *pd) +{ + struct request *rq; + struct list_head *entry; + request_queue_t *pdq; + + pdq = pkt_get_queue(pd->dev); + entry = &pd->pkt_queue; + while ((entry = entry->next) != &pd->pkt_queue) { + rq = list_entry(entry, struct request, queue); + list_del(&rq->queue); + pkt_do_request(pd, rq, pdq); + } + up(&pd->pkt_sem); + run_task_queue(&tq_disk); +} + +/* + * kpacketd is woken up, when writes have been queued for one of our + * registered devices + */ +int kpacketd(void *foobar) +{ + int i; + + kpacketd_running = 1; + daemonize(); + exit_files(current); + + current->session = 1; + current->pgrp = 1; + current->policy = SCHED_OTHER; + current->priority = 40; + strcpy(current->comm, "kpacketd"); + + spin_lock_irq(¤t->sigmask_lock); + siginitsetinv(¤t->blocked, sigmask(SIGKILL)); + flush_signals(current); + spin_unlock_irq(¤t->sigmask_lock); + + printk("pktcdvd: kpacketd thread started\n"); + for (;;) { + struct pktcdvd_device *pd; + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&kpacketd_wqueue, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + schedule(); + __set_current_state(TASK_RUNNING); + remove_wait_queue(&kpacketd_wqueue, &wait); + + /* + * got SIGKILL + */ + if (signal_pending(current)) + break; + + for (i = 0; i < MAX_WRITERS; i++) { + pd = &pkt_devs[i]; + if (!pd->dev) + continue; + down(&pd->pkt_sem); + if (!list_empty(&pd->pkt_queue)) + pkt_handle_queue(pd); + else + up(&pd->pkt_sem); + } + + } + printk("pktcdvd: kpacketd thread exiting\n"); + kpacketd_running = 0; + return 0; +} + +/* + * our request function. we empty the request list in one go and queue + * requests for the writer. three main scenarios here: + * + * - reads are just tossed directly to the device, we don't care. + * - writes that fit exactly with the packet size are tossed directly at + * the device. our merge functions have already made sure that a request + * does not span two packet boundaries. + * - write does not cover entire packet span -- invoke pkt_gather_data + * to get the blocks that are missing. + * + * all writes are initialized with pkt_init_bh so that we have a chance + * to recover and remap a failed write. + */ +static void pkt_request(request_queue_t *q) +{ + struct pktcdvd_device *pd = (struct pktcdvd_device *) q->queuedata; + struct request *rq; + request_queue_t *pdq = NULL; + int run_kpacketd = 0; + + pdq = __pkt_get_queue(pd->dev); + spin_unlock_irq(io_request_lock); + down(&pd->pkt_sem); + + /* + * eat the whole queue + */ +next_rq: + spin_lock_irq(io_request_lock); + if (list_empty(&q->queue_head)) { + up(&pd->pkt_sem); + run_task_queue(&tq_disk); + if (run_kpacketd) + wake_up(&kpacketd_wqueue); + return; + } + + rq = blkdev_entry_next_request(&q->queue_head); + blkdev_dequeue_request(rq); + + pdq = __pkt_get_queue(pd->dev); + rq->rq_dev = pd->dev; + + spin_unlock_irq(&io_request_lock); + + if (rq->cmd == READ) { + pkt_queue_request(pdq, rq); + goto next_rq; + } + + /* + * UDF had a bug, where it submitted a write to a ro file + * system, this is just to prevent accidents like that from + * happening again + */ + if (test_bit(PACKET_READONLY, &pd->flags) && rq->cmd == WRITE) { + pkt_kill_request(rq, 0, pd->name); + goto next_rq; + } + + /* paranoia... */ + if (!pkt_same_zone(pd, rq)) { + printk("merging is wrong!\n"); + printk("rq %lu to %lu\n", rq->sector, rq->nr_sectors); + printk("bh %lu to %lu\n", rq->bh->b_rsector, rq->bhtail->b_rsector); + BUG(); + } + + /* + * ok, the request is valid and for pd, queue it + */ + list_add_tail(&rq->queue, &pd->pkt_queue); + run_kpacketd = 1; + goto next_rq; +} + +static void pkt_print_settings(struct pktcdvd_device *pd) +{ + printk("pktcdvd: %s packets, ", pd->settings.fp ? "Fixed" : "Variable"); + printk("%u blocks / packet, ", pd->settings.size >> 2); + printk("Mode-%c disc\n", pd->settings.block_mode == 8 ? '1' : '2'); +} + +static void dump_buffer(unsigned char *buf, int len) +{ + int i; + for (i = 0; i < len; i++) + printk("%02x ", buf[i]); + printk("\n"); +} + +/* + * A generic sense dump / resolve mechanism should be implemented across + * all ATAPI + SCSI devices. + */ +static void pkt_dump_sense(struct request_sense *sense) +{ + char *info[9] = { "No sense", "Recovered error", "Not ready", + "Medium error", "Hardware error", "Illegal request", + "Unit attention", "Data protect", "Blank check" }; + + if (sense == NULL) + return; + + if (sense->sense_key > 8) { + printk("pktcdvd: sense invalid\n"); + return; + } + + printk("pktcdvd: sense category %s ", info[sense->sense_key]); + printk("asc(%02x), ascq(%02x)\n", sense->asc, sense->ascq); +} + +/* + * write mode select package based on pd->settings + */ +static int pkt_write_settings(struct pktcdvd_device *pd) +{ + write_param_page *wp; + char buffer[128]; + struct cdrom_generic_command cgc; + int ret, offset, size; + + memset(buffer, 0, sizeof(buffer)); + init_cdrom_command(&cgc, buffer, sizeof(*wp), CGC_DATA_READ); + if ((ret = cdrom_mode_sense(pd->cdi, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) + return ret; + + size = 2 + ((buffer[0] << 8) | (buffer[1] & 0xff)); + pd->mode_offset = offset = (buffer[6] << 8) | (buffer[7] & 0xff); + /* paranoia */ + if (size > sizeof(buffer)) { + printk("pktcdvd: auch, mode page too big...\n"); + BUG(); + } + + /* now get it all */ + init_cdrom_command(&cgc, buffer, size, CGC_DATA_READ); + if ((ret = cdrom_mode_sense(pd->cdi, &cgc, GPMODE_WRITE_PARMS_PAGE, 0))) + return ret; + + /* write page is offset header + block descriptor length */ + wp = (write_param_page *) &buffer[sizeof(struct mode_page_header) + offset]; + + wp->fp = pd->settings.fp; + wp->track_mode = pd->settings.track_mode; + wp->write_type = pd->settings.write_type; + wp->data_block_type = pd->settings.block_mode; + + wp->multi_session = 0; + +#ifdef PACKET_USE_LS + wp->link_size = 7; + wp->ls_v = 1; +#endif + + if (wp->data_block_type == 8) { + wp->session_format = 0; + wp->subhdr2 = 0x20; + } else if (wp->data_block_type == 10) { + wp->session_format = 0x20; + wp->subhdr2 = 8; + /* humor me... */ +#if 0 + wp->mcn[0] = 0x80; + memcpy(&wp->mcn[1], PACKET_MCN, sizeof(wp->mcn) - 1); +#endif + } else { + /* paranoia */ + printk("pktcdvd: some how write mode got screwed (%d)\n", + wp->data_block_type); + return 1; + } + wp->packet_size = cpu_to_be32(pd->settings.size >> 2); + + cgc.buflen = cgc.cmd[8] = size; + if ((ret = cdrom_mode_select(pd->cdi, &cgc))) { + pkt_dump_sense(cgc.sense); + dump_buffer(cgc.buffer, size); + return ret; + } + + pkt_print_settings(pd); + return 0; +} + +/* + * 0 -- we can write to this track, 1 -- we can't + */ +static int pkt_good_track(track_information *ti) +{ + /* only good for CD-RW at the moment, not DVD-RW */ + + /* FIXME: only for FP */ + if (ti->fp == 0) + return 0; + + /* "good" settings as per Mt Fuji. */ + if (ti->rt == 0 && ti->blank == 0 && ti->packet == 1) + return 0; + + if (ti->rt == 0 && ti->blank == 1 && ti->packet == 1) + return 0; + + if (ti->rt == 1 && ti->blank == 0 && ti->packet == 1) + return 0; + + printk("pktcdvd: bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet); + return 1; +} + +/* + * 0 -- we can write to this disc, 1 -- we can't + */ +static int pkt_good_disc(struct pktcdvd_device *pd, disc_information *di) +{ + /* for disc type 0xff we should probably reserve a new track. + * but i'm not sure, should we leave this to user apps? probably. + */ + if (di->disc_type == 0xff) { + printk("pktcdvd: Unknown disc. No track?\n"); + return 1; + } + + if (di->disc_type != 0x20 && di->disc_type != 0) { + printk("pktcdvd: Wrong disc type (%x)\n", di->disc_type); + return 1; + } + + if (di->erasable == 0) { + printk("pktcdvd: Disc not erasable\n"); + return 1; + } + + if (pd->track_status == PACKET_SESSION_RESERVED) { + printk("pktcdvd: Can't write to last track (reserved)\n"); + return 1; + } + + return 0; +} + +static int pkt_probe_settings(struct pktcdvd_device *pd) +{ + disc_information di; + track_information ti; + int ret, track; + + memset(&di, 0, sizeof(disc_information)); + memset(&ti, 0, sizeof(track_information)); + + if ((ret = cdrom_get_disc_info(pd->dev, &di))) { + printk("failed get_disc\n"); + return ret; + } + + pd->disc_status = di.disc_status; + pd->track_status = di.border_status; + + if (pkt_good_disc(pd, &di)) + return -ENXIO; + + printk("pktcdvd: inserted media is CD-R%s\n", di.erasable ? "W" : ""); + pd->type = di.erasable ? PACKET_CDRW : PACKET_CDR; + + track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */ + if ((ret = cdrom_get_track_info(pd->dev, track, 1, &ti))) { + printk("pktcdvd: failed get_track\n"); + return ret; + } + + if (pkt_good_track(&ti)) { + printk("pktcdvd: can't write to this track\n"); + return -ENXIO; + } + + /* we keep packet size in 512 byte units, makes it easier to + * deal with request calculations. + */ + pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2; + if (pd->settings.size == 0) { + printk("pktcdvd: detected zero packet size!\n"); + pd->settings.size = 32; + } + pd->settings.fp = ti.fp; + pd->offset = (be32_to_cpu(ti.track_start) << 2) % pd->settings.size; + + if (ti.nwa_v) { + pd->nwa = be32_to_cpu(ti.next_writable); + set_bit(PACKET_NWA_VALID, &pd->flags); + } + + /* + * in theory we could use lra on -RW media as well and just zero + * blocks that haven't been written yet, but in practice that + * is just a no-go. we'll use that for -R, naturally. + */ + if (ti.lra_v) { + pd->lra = be32_to_cpu(ti.last_rec_address); + set_bit(PACKET_LRA_VALID, &pd->flags); + } else { + pd->lra = 0xffffffff; + set_bit(PACKET_LRA_VALID, &pd->flags); + } + + /* fine for now */ + pd->settings.link_loss = 7; + pd->settings.write_type = 0; /* packet */ + pd->settings.track_mode = ti.track_mode; + + /* mode1 or mode2 */ + switch (ti.data_mode) { + case 1: pd->settings.block_mode = 8; break; + case 2: pd->settings.block_mode = 10; break; + default: printk("pktcdvd: unknown data mode\n"); return 1; + } + return 0; +} + +/* + * flush the drive cache to media + */ +static int pkt_flush_cache(struct pktcdvd_device *pd) +{ + struct cdrom_generic_command cgc; + struct cdrom_device_info *cdi = pd->cdi; + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = GPCMD_FLUSH_CACHE; + + /* + * the IMMED bit -- we default to not setting it, although that + * would allow a much faster close + */ +#if 0 + cgc.cmd[1] = 1 << 1; +#endif + return cdi->ops->generic_packet(cdi, &cgc); +} + +/* + * Returns drive current write speed + */ +static int pkt_get_speed(struct pktcdvd_device *pd) +{ + struct cdrom_generic_command cgc; + unsigned char buf[64]; + int ret, offset; + + memset(buf, 0, sizeof(buf)); + init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_UNKNOWN); + + ret = cdrom_mode_sense(pd->cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0); + if (ret) { + cgc.buflen = pd->mode_offset + buf[pd->mode_offset + 9] + 2 + + sizeof(struct mode_page_header); + ret = cdrom_mode_sense(pd->cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0); + if (ret) + return ret; + } + + offset = pd->mode_offset + 28; + pd->speed = ((buf[offset] << 8) | buf[offset + 1]) / 0xb0; + + return 0; +} + +/* + * speed is given as the normal factor, e.g. 4 for 4x + */ +static int pkt_set_speed(struct pktcdvd_device *pd, unsigned speed) +{ + struct cdrom_generic_command cgc; + struct cdrom_device_info *cdi = pd->cdi; + unsigned read_speed; + + speed = speed * 0xb0; + + /* + * we set read and write time so that read spindle speed is one and + * a half as fast as write. although a drive can typically read much + * faster than write, this minimizes the spin up/down when we write + * and gather data. maybe 1/1 factor is faster, needs a bit of testing. + */ + read_speed = (speed * 3) >> 1; + + init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE); + cgc.cmd[0] = 0xbb; + cgc.cmd[2] = (read_speed >> 8) & 0xff; + cgc.cmd[3] = read_speed & 0xff; + cgc.cmd[4] = (speed >> 8) & 0xff; + cgc.cmd[5] = speed & 0xff; + + return cdi->ops->generic_packet(cdi, &cgc); +} + +/* + * Give me full power, Captain + */ +static int pkt_max_speed(struct pktcdvd_device *pd) +{ + disc_information di; + int ret; + + /* + * FIXME: do proper unified cap page, also, this isn't proper + * Mt Fuji, but I think we can safely assume all drives support + * it. A hell of a lot more than support the GET_PERFORMANCE + * command (besides, we also use the old set speed command, + * not the streaming feature). + */ + if ((ret = pkt_set_speed(pd, 8))) + return ret; + + /* + * just do something with the disc -- next read will contain the + * maximum speed with this media + */ + if ((ret = cdrom_get_disc_info(pd->dev, &di))) + return ret; + + if ((ret = pkt_get_speed(pd))) { + printk("pktcdvd: failed get speed\n"); + return ret; + } + + DPRINTK("pktcdvd: speed (R/W) %u/%u\n", (pd->speed * 3) / 2, pd->speed); + return 0; +} + +#if 0 +static int pkt_track_capacity(struct pktcdvd_device *pd) +{ + disc_information di; + track_information ti; + int l_track, i, ret; + unsigned long size = 0; + + memset(&di, 0, sizeof(disc_information)); + memset(&ti, 0, sizeof(track_information)); + + if ((ret = cdrom_get_disc_info(pd->dev, &di))) { + DPRINTK("failed get_disc\n"); + return ret; + } + + l_track = di.last_track_lsb | di.last_track_msb >> 8; + DPRINTK("pktcdvd: last track %d\n", l_track); + for (i = di.n_first_track; i <= l_track; i++) { + if ((ret = cdrom_get_track_info(pd->dev, i, 1, &ti))) { + DPRINTK("pktcdvd: failed get_track\n"); + return ret; + } + size += be32_to_cpu(ti.track_size); + } + pkt_sizes[MINOR(pd->pkt_dev)] = size << 1; + return 0; +} + +static int pkt_set_capacity(struct pktcdvd_device *pd) +{ + struct cdrom_generic_command cgc; + struct cdrom_device_info *cdi = pd->cdi; + struct cdvd_capacity cap; + int ret; + + init_cdrom_command(&cgc, &cap, sizeof(cap)); + cgc.cmd[0] = GPCMD_READ_CDVD_CAPACITY; + if ((ret = cdi->ops->generic_packet(cdi, &cgc))) + return ret; + + /* + * We should probably give up if read capacity fails, since then + * then disc is not ready to be written to -- for now I use + * raw devices and this is fine. + */ + pkt_sizes[MINOR(pd->pkt_dev)] = be32_to_cpu(cap.lba) << 1; + return 0; +} +#endif + +static int pkt_open_write(struct pktcdvd_device *pd) +{ + int ret; + + if ((ret = pkt_probe_settings(pd))) { + DPRINTK("pktcdvd: %s failed probe\n", pd->name); + return -EIO; + } + + if ((ret = pkt_write_settings(pd))) { + DPRINTK("pktcdvd: %s failed saving write settings\n", pd->name); + return -EIO; + } + + if ((ret = pkt_max_speed(pd))) { + DPRINTK("pktcdvd: %s couldn't set write speed\n", pd->name); + return -EIO; + } + return 0; +} + +/* + * called at open time. return 1 if the device can only be opened read-only. + */ +static int pkt_open_dev(struct pktcdvd_device *pd, int write) +{ + int ret; + long lba; + + if (!pd->dev) + return 0; + + if ((ret = cdrom_get_last_written(pd->dev, &lba))) + return ret; + + pkt_sizes[MINOR(pd->pkt_dev)] = 1 + (lba << 1); + + if (write) { + if ((ret = pkt_open_write(pd))) + return ret; + clear_bit(PACKET_READONLY, &pd->flags); + } else { + set_bit(PACKET_READONLY, &pd->flags); + } + + printk("pktcdvd: %luKB available on disc\n", lba << 1); + return 0; +} + +/* + * called the device is closed. makes sure that the device flushes + * the internal cache before we close. + */ +static void pkt_release_dev(struct pktcdvd_device *pd, int flush) +{ + if (flush) + if (pkt_flush_cache(pd)) + DPRINTK("pktcdvd: %s not flushing cache\n", pd->name); + + atomic_dec(&pd->refcnt); +} + +static int pkt_open(struct inode *inode, struct file *file) +{ + struct pktcdvd_device *pd = NULL; + int ret = 0; + + VPRINTK("pktcdvd: entering open\n"); + + MOD_INC_USE_COUNT; + + /* should this really be necessary?? */ + if (!inode) { + MOD_DEC_USE_COUNT; + return -EINVAL; + } + + if (MINOR(inode->i_rdev) >= MAX_WRITERS) { + printk("pktcdvd: max %d writers supported\n", MAX_WRITERS); + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + pd = &pkt_devs[MINOR(inode->i_rdev)]; + atomic_inc(&pd->refcnt); + if (!pd->dev && (file->f_mode & FMODE_WRITE)) { + printk("pktcdvd: open RDONLY to setup device\n"); + ret = -ENXIO; + goto out; + } + + ret = -EBUSY; + if (atomic_read(&pd->refcnt) > 1 && file->f_mode & FMODE_WRITE) + goto out; + + ret = -EIO; + if (pkt_open_dev(pd, file->f_mode & FMODE_WRITE)) + goto out; + + return 0; +out: + DPRINTK("pktcdvd: failed open\n"); + MOD_DEC_USE_COUNT; + atomic_dec(&pd->refcnt); + return ret; +} + +static int pkt_close(struct inode *inode, struct file *file) +{ + struct pktcdvd_device *pd = &pkt_devs[MINOR(inode->i_rdev)]; + int ret = 0; + + if (pd->dev) + pkt_release_dev(pd, 1); + MOD_DEC_USE_COUNT; + return ret; +} + +/* + * pktcdvd i/o elevator + * + * rules: always merge whenever possible, and support hole merges + */ +static void pkt_elevator(struct request *rq, elevator_t *elevator, + struct list_head *real_head, struct list_head *head, + int orig_latency) +{ + struct list_head *entry = real_head; + struct request *tmp; + int pass = 0; + + if (list_empty(head)) { + list_add(&rq->queue, entry); + return; + } + + while ((entry = entry->prev) != head) { + tmp = blkdev_entry_to_request(entry); + if (IN_ORDER(tmp, rq) || (pass && !IN_ORDER(tmp, blkdev_next_request(tmp)))) + break; + pass = 1; + } + list_add(&rq->queue, entry); +} + +static int pkt_elevator_merge(request_queue_t *q, struct request **rq, + struct buffer_head *bh, int rw, + int *max_sectors, int *max_segments) +{ + struct list_head *entry, *head = &q->queue_head; + unsigned int count = bh->b_size >> 9; + + if (q->head_active && !q->plugged) + head = head->next; + + entry = head; + while ((entry = entry->prev) != head) { + *rq = blkdev_entry_to_request(entry); + if ((*rq)->sem) + continue; + if ((*rq)->cmd != rw) + continue; + if ((*rq)->nr_sectors + count > *max_sectors) + continue; + if ((*rq)->rq_dev != bh->b_rdev) + continue; + if ((*rq)->sector + (*rq)->nr_sectors == bh->b_rsector) + return ELEVATOR_BACK_MERGE; + if ((*rq)->sector - count == bh->b_rsector) + return ELEVATOR_FRONT_MERGE; + if (q->hole_merge_fn(q, *rq, bh, *max_segments)) + return ELEVATOR_HOLE_MERGE; + } + return ELEVATOR_NO_MERGE; +} + +static void pkt_init_queue(struct pktcdvd_device *pd) +{ + request_queue_t *q = &pd->queue; + + blk_init_queue(q, pkt_request); + blk_queue_headactive(q, 0); + elevator_init(&q->elevator, ELEVATOR_PKTCDVD); + q->front_merge_fn = pkt_front_merge_fn; + q->back_merge_fn = pkt_back_merge_fn; + q->hole_merge_fn = pkt_hole_merge_fn; + q->merge_requests_fn = pkt_merge_requests_fn; + q->queuedata = pd; +} + +static int pkt_new_dev(struct pktcdvd_device *pd, kdev_t dev) +{ + struct cdrom_device_info *cdi; + request_queue_t *q; + int i; + + for (i = 0; i < MAX_WRITERS; i++) { + if (pkt_devs[i].dev == dev) { + printk("pktcdvd: %s already setup\n", kdevname(dev)); + return -EBUSY; + } + } + + for (i = 0; i < MAX_WRITERS; i++) + if (pd == &pkt_devs[i]) + break; + + if (i == MAX_WRITERS) { + printk("pktcdvd: max %d writers supported\n", MAX_WRITERS); + return -ENXIO; + } + + cdi = cdrom_find_device(dev); + if (cdi == NULL) { + printk("pktcdvd: %s is not a CD-ROM\n", kdevname(dev)); + return -ENXIO; + } + + MOD_INC_USE_COUNT; + + memset(pd, 0, sizeof(struct pktcdvd_device)); + set_blocksize(dev, CD_FRAMESIZE); + pd->cdi = cdi; + pd->dev = dev; + pd->pkt_dev = MKDEV(PACKET_MAJOR, i); + strncpy(pd->name, cdi->name, sizeof(pd->name) - 1); + spin_lock_init(&pd->lock); + init_MUTEX(&pd->pkt_sem); + INIT_LIST_HEAD(&pd->pkt_queue); + atomic_set(&pd->refcnt, 0); + + /* + * store device merge functions (SCSI uses them to build + * scatter-gather tables) + */ + spin_lock_irq(&io_request_lock); + pkt_init_queue(pd); + q = __pkt_get_queue(dev); + pd->front_merge_fn = q->front_merge_fn; + pd->back_merge_fn = q->back_merge_fn; + pd->merge_requests_fn = q->merge_requests_fn; + spin_unlock_irq(&io_request_lock); + + DPRINTK("pktcdvd: writer %s sucessfully registered\n", pd->name); + return 0; +} + +static int pkt_lock_tray(struct pktcdvd_device *pd, int lock) +{ + struct cdrom_device_info *cdi = pd->cdi; + return cdi->ops->lock_door(cdi, !!lock); +} + +/* + * arg contains file descriptor of CD-ROM device. + */ +static int pkt_setup_dev(struct pktcdvd_device *pd, unsigned int arg) +{ + struct inode *inode; + struct file *file; + int ret; + + if ((file = fget(arg)) == NULL) { + printk("pktcdvd: bad file descriptor passed\n"); + return -EBADF; + } + + ret = -EINVAL; + if ((inode = file->f_dentry->d_inode) == NULL) { + printk("pktcdvd: huh? file descriptor contains no inode?\n"); + goto out; + } + ret = -ENOTBLK; + if (!S_ISBLK(inode->i_mode)) { + printk("pktcdvd: device is not a block device (duh)\n"); + goto out; + } + ret = blkdev_get(inode->i_bdev, file->f_mode, file->f_flags, BDEV_FILE); + if (ret) + goto out; + ret = -EROFS; + if (IS_RDONLY(inode)) { + printk("pktcdvd: Can't write to read-only dev\n"); + goto out; + } + if ((ret = pkt_new_dev(pd, inode->i_rdev))) { + printk("pktcdvd: all booked up\n"); + goto out; + } + + pd->pkt_dentry = dget(file->f_dentry); + atomic_inc(&pd->refcnt); + + if ((ret = pkt_lock_tray(pd, 1))) { + printk("pktcdvd: can't lock drive tray\n"); + atomic_dec(&pd->refcnt); + } +out: + fput(file); + return ret; +} + +static int pkt_remove_dev(struct pktcdvd_device *pd) +{ + /* will also invalidate buffers for CD-ROM */ + blkdev_put(pd->pkt_dentry->d_inode->i_bdev, BDEV_FILE); + dput(pd->pkt_dentry); + + /* Unlock CD-ROM device */ + (void) pkt_lock_tray(pd, 0); + destroy_buffers(pd->pkt_dev); + blk_cleanup_queue(&pd->queue); + DPRINTK("pktcdvd: writer %s unregistered\n", pd->name); + memset(pd, 0, sizeof(struct pktcdvd_device)); + MOD_DEC_USE_COUNT; + return 0; +} + +static int pkt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct pktcdvd_device *pd = &pkt_devs[MINOR(inode->i_rdev)]; + + if (cmd != PACKET_SETUP_DEV) { + if (!pd->dev) { + DPRINTK("pktcdvd: dev not setup\n"); + return -ENXIO; + } + } + + switch (cmd) { + case PACKET_GET_STATS: + if (copy_to_user(&arg, &pd->stats, sizeof(struct packet_stats))) + return -EFAULT; + + case PACKET_SETUP_DEV: + if (pd->dev) { + printk("pktcdvd: dev already setup\n"); + return -EBUSY; + } + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + return pkt_setup_dev(pd, arg); + + case PACKET_TEARDOWN_DEV: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (atomic_read(&pd->refcnt) != 1) + return -EBUSY; + return pkt_remove_dev(pd); + + case BLKGETSIZE: + return put_user(blk_size[PACKET_MAJOR][MINOR(inode->i_rdev)] << 1, (long *)arg); + + case BLKROSET: + case BLKROGET: + case BLKSSZGET: + case BLKRASET: + case BLKRAGET: + case BLKFLSBUF: + return blk_ioctl(inode->i_rdev, cmd, arg); + + /* forward selected CDROM ioctls to CD-ROM, for UDF */ + case CDROMMULTISESSION: + case CDROMREADTOCENTRY: + case CDROM_LAST_WRITTEN: + case CDROM_SEND_PACKET: + return pd->cdi->ops->dev_ioctl(pd->cdi, cmd, arg); + + default: + printk("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd); + return -ENOTTY; + } + return 0; +} + +static struct block_device_operations pktcdvd_ops = { + open: pkt_open, + release: pkt_close, + ioctl: pkt_ioctl, +}; + +static int pkt_init(void) +{ + int ret; + + devfs_register(NULL, "pktcdvd", 0, DEVFS_FL_DEFAULT, PACKET_MAJOR, 0, + S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, &pktcdvd_ops, NULL); + if (devfs_register_blkdev(PACKET_MAJOR, "pktcdvd", &pktcdvd_ops)) { + printk("unable to register pktcdvd device\n"); + return -EIO; + } + ret = -ENOMEM; + pkt_sizes = kmalloc(MAX_WRITERS * sizeof(int), GFP_KERNEL); + if (pkt_sizes == NULL) + goto out; + + pkt_blksize = kmalloc(MAX_WRITERS * sizeof(int), GFP_KERNEL); + if (pkt_blksize == NULL) + goto out; + + pkt_readahead = kmalloc(MAX_WRITERS * sizeof(int), GFP_KERNEL); + if (pkt_readahead == NULL) + goto out; + + pkt_devs = kmalloc(MAX_WRITERS * sizeof(struct pktcdvd_device), GFP_KERNEL); + if (pkt_devs == NULL) + goto out; + + kpacketd_pid = kernel_thread(kpacketd, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + if (kpacketd_pid < 0) { + printk("pktcdvd: can't start kernel thread\n"); + ret = kpacketd_pid; + goto out; + } + + memset(pkt_devs, 0, MAX_WRITERS * sizeof(struct pktcdvd_device)); + memset(pkt_sizes, 0, MAX_WRITERS * sizeof(int)); + memset(pkt_blksize, 0, MAX_WRITERS * sizeof(int)); + memset(pkt_readahead, 0, MAX_WRITERS * sizeof(int)); + + blk_size[PACKET_MAJOR] = pkt_sizes; + blksize_size[PACKET_MAJOR] = pkt_blksize; + max_readahead[PACKET_MAJOR] = pkt_readahead; + read_ahead[PACKET_MAJOR] = 0; + set_blocksize(MKDEV(PACKET_MAJOR, 0), CD_FRAMESIZE); + + blk_dev[PACKET_MAJOR].queue = pkt_my_queue; + + DPRINTK("pktcdvd: %s\n", VERSION_CODE); + return 0; + +out: + if (ret == -ENOMEM) + printk("pktcdvd: out of memory\n"); + devfs_unregister(devfs_find_handle(NULL, "pktcdvd", 0, 0, 0, + DEVFS_SPECIAL_BLK, 0)); + devfs_unregister_blkdev(PACKET_MAJOR, "pktcdvd"); + if (pkt_devs) + kfree(pkt_devs); + if (pkt_sizes) + kfree(pkt_sizes); + if (pkt_blksize) + kfree(pkt_blksize); + if (pkt_readahead) + kfree(pkt_readahead); + return ret; +} + +static void __exit pkt_exit(void) +{ + int ret; + + devfs_unregister(devfs_find_handle(NULL, "pktcdvd", 0, 0, 0, + DEVFS_SPECIAL_BLK, 0)); + devfs_unregister_blkdev(PACKET_MAJOR, "pktcdvd"); + + if (!(ret = kill_proc(kpacketd_pid, SIGKILL, 1))) { + int count = 10; + while (kpacketd_running && --count) { + __set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 10); + } + if (!count) + printk("pkt_exit: can't kill kpacketd thread\n"); + } + + kfree(pkt_sizes); + kfree(pkt_blksize); + kfree(pkt_devs); + kfree(pkt_readahead); +} + +MODULE_DESCRIPTION("Packet writing layer for CD/DVD drives"); +MODULE_AUTHOR("Jens Axboe "); + +module_init(pkt_init); +module_exit(pkt_exit); + diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/char/keyboard.c linux/drivers/char/keyboard.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/char/keyboard.c Tue Feb 15 00:31:14 2000 +++ linux/drivers/char/keyboard.c Tue Jun 13 16:30:11 2000 @@ -42,6 +42,9 @@ #include #include #include +#if defined(CONFIG_KDB) +#include +#endif #define SIZE(x) (sizeof(x)/sizeof((x)[0])) @@ -249,6 +252,13 @@ up_flag = kbd_unexpected_up(keycode); } else rep = test_and_set_bit(keycode, key_down); + +#if defined(CONFIG_KDB) + if (!up_flag && (keycode == E1_PAUSE)) { + kdb(KDB_REASON_KEYBOARD, 0, kbd_pt_regs); + return; + } +#endif /* CONFIG_KDB */ #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ if (keycode == SYSRQ_KEY) { diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/char/serial.c linux/drivers/char/serial.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/char/serial.c Tue Jun 20 03:24:40 2000 +++ linux/drivers/char/serial.c Tue Jun 13 16:30:11 2000 @@ -200,6 +200,19 @@ #include #endif +#if defined(CONFIG_KDB) +#include +/* + * kdb_serial_line records the serial line number of the + * first serial console. kdb_info will be set upon receipt + * of the first ^A (which cannot happen until the port is + * opened and the interrupt handler attached). To enter + * kdb before this on a serial console-only system, you must + * use the 'kdb' flag to lilo and set the appropriate breakpoints. + */ + +static int kdb_serial_line = -1; +#endif /* CONFIG_KDB */ /* * All of the compatibilty code so we can compile serial.c against * older kernels is hidden in serial_compat.h @@ -548,6 +561,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, (kdb_eframe_t)regs); + break; + } +#endif if (tty->flip.count >= TTY_FLIPBUF_SIZE) goto ignore_char; *tty->flip.char_buf_ptr = ch; @@ -5551,6 +5571,17 @@ */ if (serial_in(info, UART_LSR) == 0xff) return -1; + +#if defined(CONFIG_KDB) + /* + * Remember the line number of the first serial + * console. We'll make this the kdb serial console too. + */ + if (kdb_serial_line == -1) { + kdb_serial_line = co->index; + kdb_port = info->port; + } +#endif /* CONFIG_KDB */ return 0; } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/ide/ide-cd.c linux/drivers/ide/ide-cd.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/ide/ide-cd.c Tue Jun 20 03:24:41 2000 +++ linux/drivers/ide/ide-cd.c Mon Jun 19 18:46:43 2000 @@ -285,9 +285,11 @@ * 4.58 May 1, 2000 - Clean up ACER50 stuff. * - Fix small problem with ide_cdrom_capacity * + * 4.99 - Added write support for packet writing. + * *************************************************************************/ -#define IDECD_VERSION "4.58" +#define IDECD_VERSION "4.99" #include #include @@ -360,6 +362,15 @@ return; } + /* + * If a read toc is executed for a CD-R or CD-RW medium where + * the first toc has not been recorded yet, it will fail with + * 05/24/00 + */ + if (failed_command && failed_command->c[0] == GPCMD_READ_TOC_PMA_ATIP) + if (sense->sense_key == 0x05 && sense->asc == 0x24) + return; + #if VERBOSE_IDE_CD_ERRORS { int i; @@ -524,8 +535,10 @@ (struct packet_command *) pc->sense, (struct request_sense *) (pc->buffer - pc->c[4])); } - if (rq->cmd == READ && !rq->current_nr_sectors) - uptodate = 1; + + if (rq->cmd == READ || rq->cmd == WRITE_PACKET) + if (!rq->current_nr_sectors) + uptodate = 1; ide_end_request (uptodate, HWGROUP(drive)); } @@ -609,7 +622,7 @@ cdrom_queue_request_sense(drive, sem, pc->sense, pc); } else { - /* Handle errors from READ requests. */ + /* Handle errors from READ and WRITE requests. */ if (sense_key == NOT_READY) { /* Tray open. */ @@ -661,11 +674,22 @@ struct packet_command *pc = (struct packet_command *) rq->buffer; unsigned long wait = 0; - /* blank and format can take an extremly long time to - * complete, if the IMMED bit was not set. + /* + * Some commands are *slow* and normally take a long time to + * complete. Usually we can use the ATAPI "disconnect" to bypass + * this, but not all commands/drives support that. Let + * ide_timer_expiry keep polling us for these. */ - if (pc->c[0] == GPCMD_BLANK || pc->c[0] == GPCMD_FORMAT_UNIT) - wait = 60*60*HZ; + switch (pc->c[0]) { + case GPCMD_BLANK: + case GPCMD_FORMAT_UNIT: + case GPCMD_RESERVE_RZONE_TRACK: + wait = WAIT_CMD; + break; + default: + wait = 0; + break; + } return wait; } @@ -677,8 +701,10 @@ called when the interrupt from the drive arrives. Otherwise, HANDLER will be called immediately after the drive is prepared for the transfer. */ -static ide_startstop_t cdrom_start_packet_command (ide_drive_t *drive, int xferlen, - ide_handler_t *handler) +static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive, + int xferlen, + ide_handler_t *handler, + int cmd) { ide_startstop_t startstop; struct cdrom_info *info = drive->driver_data; @@ -687,8 +713,15 @@ if (ide_wait_stat(&startstop, drive, 0, BUSY_STAT, WAIT_READY)) return startstop; - if (info->dma) - info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive); + if (info->dma) { + if (cmd == READ) { + info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive); + } else if (cmd == WRITE) { + info->dma = !HWIF(drive)->dmaproc(ide_dma_write, drive); + } else { + printk("ide-cd: DMA set, but not allowed\n"); + } + } /* Set up the controller registers. */ OUT_BYTE (info->dma, IDE_FEATURE_REG); @@ -718,9 +751,10 @@ by cdrom_start_packet_command. HANDLER is the interrupt handler to call when the command completes or there's data ready. */ -static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive, +static ide_startstop_t cdrom_transfer_packet_command(ide_drive_t *drive, unsigned char *cmd_buf, int cmd_len, - ide_handler_t *handler) + ide_handler_t *handler, + unsigned timeout) { if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { /* Here we should have been called after receiving an interrupt @@ -739,10 +773,10 @@ } /* Arm the interrupt handler. */ - ide_set_handler (drive, handler, WAIT_CMD, cdrom_timer_expiry); + ide_set_handler(drive, handler, timeout, cdrom_timer_expiry); /* Send the command to the device. */ - atapi_output_bytes (drive, cmd_buf, cmd_len); + atapi_output_bytes(drive, cmd_buf, cmd_len); return ide_started; } @@ -834,8 +868,8 @@ drive->name, ireason); } - cdrom_end_request (0, drive); - return -1; + cdrom_end_request(0, drive); + return 1; } /* @@ -1076,7 +1110,7 @@ (65534 / CD_FRAMESIZE) : 65535); /* Set up the command */ - memset (&pc.c, 0, sizeof (pc.c)); + memset(&pc.c, 0, sizeof (pc.c)); pc.c[0] = GPCMD_READ_10; pc.c[7] = (nframes >> 8); pc.c[8] = (nframes & 0xff); @@ -1084,7 +1118,7 @@ /* Send the command to the drive and return. */ return cdrom_transfer_packet_command(drive, pc.c, sizeof(pc.c), - &cdrom_read_intr); + &cdrom_read_intr, WAIT_CMD); } @@ -1127,7 +1161,8 @@ memset (&pc.c, 0, sizeof (pc.c)); pc.c[0] = GPCMD_SEEK; put_unaligned(cpu_to_be32(frame), (unsigned int *) &pc.c[2]); - return cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr); + return cdrom_transfer_packet_command(drive, pc.c, sizeof(pc.c), + &cdrom_seek_intr, WAIT_CMD); } static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block) @@ -1136,7 +1171,7 @@ info->dma = 0; info->start_seek = jiffies; - return cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation); + return cdrom_start_packet_command(drive, 0, cdrom_start_seek_continuation, 0); } /* Fix up a possibly partially-processed request so that we can @@ -1188,7 +1223,7 @@ info->dma = 0; /* Start sending the read request to the drive. */ - return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation); + return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation, READ); } /**************************************************************************** @@ -1303,8 +1338,8 @@ struct packet_command *pc = (struct packet_command *)rq->buffer; /* Send the command to the drive and return. */ - return cdrom_transfer_packet_command (drive, pc->c, - sizeof (pc->c), &cdrom_pc_intr); + return cdrom_transfer_packet_command(drive, pc->c, sizeof(pc->c), + &cdrom_pc_intr, WAIT_CMD); } @@ -1320,7 +1355,7 @@ len = pc->buflen; /* Start sending the command to the drive. */ - return cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation); + return cdrom_start_packet_command(drive, len, cdrom_do_pc_continuation, 0); } @@ -1381,16 +1416,186 @@ return pc->stat ? -EIO : 0; } +/* + * Write handling + */ +static inline int cdrom_write_check_ireason(ide_drive_t *drive, int len, + int ireason) +{ + /* Two notes about IDE interrupt reason here - 0 means that + * the drive wants to receive data from us, 2 means that + * the drive is expecting data from us. + */ + ireason &= 3; + + if (ireason == 2) { + /* Whoops... The drive wants to send data. */ + printk("%s: cdrom_write_intr: wrong transfer direction!\n", + drive->name); + + /* Throw some data at the drive so it doesn't hang + and quit this request. */ + while (len > 0) { + int dum = 0; + atapi_output_bytes(drive, &dum, sizeof(dum)); + len -= sizeof(dum); + } + } else { + /* Drive wants a command packet, or invalid ireason... */ + printk("%s: cdrom_write_intr: bad interrupt reason %d\n", + drive->name, ireason); + } + + cdrom_end_request(0, drive); + return 1; +} + +static ide_startstop_t cdrom_write_intr(ide_drive_t *drive) +{ + int stat, ireason, len, sectors_to_transfer; + struct cdrom_info *info = drive->driver_data; + int i, dma_error = 0, dma = info->dma; + ide_startstop_t startstop; + + struct request *rq = HWGROUP(drive)->rq; + + /* Check for errors. */ + if (dma) { + info->dma = 0; + if ((dma_error = HWIF(drive)->dmaproc(ide_dma_end, drive))) { + printk("ide-cd: write dma error\n"); + HWIF(drive)->dmaproc(ide_dma_off, drive); + } + } + + if (cdrom_decode_status(&startstop, drive, 0, &stat)) { + printk("ide-cd: write_intr decode_status bad\n"); + return startstop; + } + + if (dma) { + if (dma_error) + return ide_error(drive, "dma error", stat); + + rq = HWGROUP(drive)->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, HWGROUP(drive)); + } + return ide_stopped; + } + + /* Read the interrupt reason and the transfer length. */ + ireason = IN_BYTE(IDE_NSECTOR_REG); + len = IN_BYTE(IDE_LCYL_REG) + 256 * IN_BYTE(IDE_HCYL_REG); + + /* If DRQ is clear, the command has completed. */ + if ((stat & DRQ_STAT) == 0) { + /* If we're not done writing, complain. + * Otherwise, complete the command normally. + */ + if (rq->current_nr_sectors > 0) { + printk("%s: write_intr: data underrun (%ld blocks)\n", + drive->name, rq->current_nr_sectors); + cdrom_end_request(0, drive); + } else + cdrom_end_request(1, drive); + return ide_stopped; + } + + /* Check that the drive is expecting to do the same thing we are. */ + if (ireason & 3) + if (cdrom_write_check_ireason(drive, len, ireason)) + return ide_stopped; + + /* The number of sectors we need to read from the drive. */ + sectors_to_transfer = len / SECTOR_SIZE; + + /* Now loop while we still have data to read from the drive. DMA + * transfers will already have been complete + */ + while (sectors_to_transfer > 0) { + unsigned long flags; + + /* If we've filled the present buffer but there's another + chained buffer after it, move on. */ + if (rq->current_nr_sectors == 0 && rq->nr_sectors > 0) + cdrom_end_request(1, drive); + + atapi_output_bytes(drive, rq->buffer, rq->current_nr_sectors); + spin_lock_irqsave(&io_request_lock, flags); + rq->nr_sectors -= rq->current_nr_sectors; + rq->current_nr_sectors = 0; + rq->sector += rq->current_nr_sectors; + sectors_to_transfer -= rq->current_nr_sectors; + spin_unlock_irqrestore(&io_request_lock, flags); + } + + /* arm handler */ + ide_set_handler(drive, &cdrom_write_intr, 5 * WAIT_CMD, NULL); + return ide_started; +} + +static ide_startstop_t cdrom_start_write_cont(ide_drive_t *drive) +{ + struct packet_command pc; + struct request *rq = HWGROUP(drive)->rq; + unsigned nframes, frame; + + nframes = rq->nr_sectors >> 2; + frame = rq->sector >> 2; + +#define IDECD_WRITE_PARANOIA +#ifdef IDECD_WRITE_PARANOIA + if (nframes != 32 || frame % 32) { + printk("ide-cd: got WRONG write! %u %u\n", frame, nframes); + ide_end_request(1, HWGROUP(drive)); + return ide_stopped; + } +#endif + + memset(&pc.c, 0, sizeof(pc.c)); + /* + * we might as well use WRITE_12, but none of the device I have + * support the streaming feature anyway, so who cares. + */ + pc.c[0] = GPCMD_WRITE_10; +#if 0 + pc.c[1] = 1 << 3; +#endif + pc.c[7] = (nframes >> 8) & 0xff; + pc.c[8] = nframes & 0xff; + put_unaligned(cpu_to_be32(frame), (unsigned int *)&pc.c[2]); + + return cdrom_transfer_packet_command(drive, pc.c, sizeof(pc.c), + cdrom_write_intr, 2 * WAIT_CMD); +} + +static ide_startstop_t cdrom_start_write(ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + + info->nsectors_buffered = 0; + + /* use dma, if possible. we don't need to check more, since we + * know that the transfer is always (at least!) 2KB aligned */ + info->dma = drive->using_dma ? 1 : 0; + + /* Start sending the read request to the drive. */ + return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont, WRITE); +} + /**************************************************************************** * cdrom driver request routine. */ static ide_startstop_t -ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block) +ide_do_rw_cdrom(ide_drive_t *drive, struct request *rq, unsigned long block) { ide_startstop_t action; struct cdrom_info *info = drive->driver_data; switch (rq->cmd) { + case WRITE_PACKET: case READ: { if (CDROM_CONFIG_FLAGS(drive)->seeking) { unsigned long elpased = jiffies - info->start_seek; @@ -1407,8 +1612,12 @@ } if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) action = cdrom_start_seek (drive, block); - else - action = cdrom_start_read (drive, block); + else { + if (rq->cmd == READ) + action = cdrom_start_read(drive, block); + else + action = cdrom_start_write(drive); + } info->last_block = block; return action; } @@ -1422,9 +1631,8 @@ cdrom_end_request(1, drive); return ide_do_reset(drive); } - default: { - printk("ide-cd: bad cmd %d\n", rq -> cmd); + printk("ide-cd: bad cmd %d\n", rq->cmd); cdrom_end_request(0, drive); return ide_stopped; } @@ -1768,6 +1976,9 @@ if (stat) toc->capacity = 0x1fffff; + HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS] = (toc->capacity * SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9); + drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME; + /* Remember that we've read this stuff. */ CDROM_STATE_FLAGS (drive)->toc_valid = 1; @@ -1813,8 +2024,9 @@ pc.c[2] = (speed >> 8) & 0xff; /* Read Drive speed in kbytes/second LSB */ pc.c[3] = speed & 0xff; - if ( CDROM_CONFIG_FLAGS(drive)->cd_r || - CDROM_CONFIG_FLAGS(drive)->cd_rw ) { + if (CDROM_CONFIG_FLAGS(drive)->cd_r || + CDROM_CONFIG_FLAGS(drive)->cd_rw || + CDROM_CONFIG_FLAGS(drive)->dvd_r) { /* Write Drive speed in kbytes/second MSB */ pc.c[4] = (speed >> 8) & 0xff; /* Write Drive speed in kbytes/second LSB */ @@ -2066,11 +2278,11 @@ struct request_sense sense; int ret; - toc = info->toc; - if (!CDROM_STATE_FLAGS(drive)->toc_valid || toc == NULL) + if (!CDROM_STATE_FLAGS(drive)->toc_valid || info->toc == NULL) if ((ret = cdrom_read_toc(drive, &sense))) return ret; + toc = info->toc; ms_info->addr.lba = toc->last_session_lba; ms_info->xa_flag = toc->xa_flag; @@ -2120,51 +2332,44 @@ } } - -static -int ide_cdrom_open_real (struct cdrom_device_info *cdi, int purpose) +static int ide_cdrom_open_real(struct cdrom_device_info *cdi, int purpose) { return 0; } - /* * Close down the device. Invalidate all cached blocks. */ - -static -void ide_cdrom_release_real (struct cdrom_device_info *cdi) +static void ide_cdrom_release_real(struct cdrom_device_info *cdi) { } - - /**************************************************************************** * Device initialization. */ -static -struct cdrom_device_ops ide_cdrom_dops = { - ide_cdrom_open_real, /* open */ - ide_cdrom_release_real, /* release */ - ide_cdrom_drive_status, /* drive_status */ - ide_cdrom_check_media_change_real, /* media_changed */ - ide_cdrom_tray_move, /* tray_move */ - ide_cdrom_lock_door, /* lock_door */ - ide_cdrom_select_speed, /* select_speed */ - NULL, /* select_disc */ - ide_cdrom_get_last_session, /* get_last_session */ - ide_cdrom_get_mcn, /* get_mcn */ - ide_cdrom_reset, /* reset */ - ide_cdrom_audio_ioctl, /* audio_ioctl */ - ide_cdrom_dev_ioctl, /* dev_ioctl */ - CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED - | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN - | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_IOCTLS - | CDC_DRIVE_STATUS | CDC_CD_R | CDC_CD_RW | CDC_DVD - | CDC_DVD_R| CDC_DVD_RAM | CDC_GENERIC_PACKET, /* capability */ - 0, /* n_minors */ - ide_cdrom_packet +static struct cdrom_device_ops ide_cdrom_dops = { + open: ide_cdrom_open_real, + release: ide_cdrom_release_real, + drive_status: ide_cdrom_drive_status, + media_changed: ide_cdrom_check_media_change_real, + tray_move: ide_cdrom_tray_move, + lock_door: ide_cdrom_lock_door, + select_speed: ide_cdrom_select_speed, + get_last_session: ide_cdrom_get_last_session, + get_mcn: ide_cdrom_get_mcn, + reset: ide_cdrom_reset, + audio_ioctl: ide_cdrom_audio_ioctl, + dev_ioctl: ide_cdrom_dev_ioctl, + capability: CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | + CDC_SELECT_SPEED | CDC_SELECT_DISC | + CDC_MULTI_SESSION | CDC_MCN | + CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | + CDC_IOCTLS | CDC_DRIVE_STATUS | CDC_CD_R | + CDC_CD_RW | CDC_DVD | CDC_DVD_R| CDC_DVD_RAM | + CDC_GENERIC_PACKET, + n_minors: 0, + generic_packet: ide_cdrom_packet }; static int ide_cdrom_register (ide_drive_t *drive, int nslots) @@ -2316,15 +2521,11 @@ printk(" %dX", CDROM_CONFIG_FLAGS(drive)->max_speed); printk(" %s", CDROM_CONFIG_FLAGS(drive)->dvd ? "DVD-ROM" : "CD-ROM"); - if (CDROM_CONFIG_FLAGS (drive)->dvd_r|CDROM_CONFIG_FLAGS (drive)->dvd_ram) - printk (" DVD%s%s", - (CDROM_CONFIG_FLAGS (drive)->dvd_r)? "-R" : "", - (CDROM_CONFIG_FLAGS (drive)->dvd_ram)? "AM" : ""); - - if (CDROM_CONFIG_FLAGS (drive)->cd_r|CDROM_CONFIG_FLAGS (drive)->cd_rw) - printk (" CD%s%s", - (CDROM_CONFIG_FLAGS (drive)->cd_r)? "-R" : "", - (CDROM_CONFIG_FLAGS (drive)->cd_rw)? "/RW" : ""); + if (CDROM_CONFIG_FLAGS(drive)->dvd_r || CDROM_CONFIG_FLAGS(drive)->dvd_ram) + printk (" DVD-R%s", (CDROM_CONFIG_FLAGS (drive)->dvd_ram)? "AM" : ""); + + if (CDROM_CONFIG_FLAGS(drive)->cd_r ||CDROM_CONFIG_FLAGS(drive)->cd_rw) + printk (" CD-R%s", (CDROM_CONFIG_FLAGS (drive)->cd_rw)? "/RW" : ""); if (CDROM_CONFIG_FLAGS (drive)->is_changer) printk (" changer w/%d slots", nslots); @@ -2347,7 +2548,7 @@ int major = HWIF(drive)->major; int minor = drive->select.b.unit << PARTN_BITS; - ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL); + ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 1024, &read_ahead[major], NULL); ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL); ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL); ide_add_setting(drive, "dsc_overlap", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->dsc_overlap, NULL); @@ -2361,7 +2562,7 @@ int minor = drive->select.b.unit << PARTN_BITS; int nslots; - set_device_ro(MKDEV(HWIF(drive)->major, minor), 1); + set_device_ro(MKDEV(HWIF(drive)->major, minor), 0); set_blocksize(MKDEV(HWIF(drive)->major, minor), CD_FRAMESIZE); drive->special.all = 0; @@ -2507,12 +2708,11 @@ MOD_INC_USE_COUNT; if (info->buffer == NULL) info->buffer = (char *) kmalloc(SECTOR_BUFFER_SIZE, GFP_KERNEL); - rc = cdrom_fops.open (ip, fp); - if (rc) { + if ((rc = cdrom_fops.open(ip, fp))) { drive->usage--; MOD_DEC_USE_COUNT; } - return rc; + return 0; } static @@ -2567,7 +2767,7 @@ struct cdrom_info *info = drive->driver_data; struct cdrom_device_info *devinfo = &info->devinfo; - if (ide_unregister_subdriver (drive)) + if (ide_unregister_subdriver(drive)) return 1; if (info->buffer != NULL) kfree(info->buffer); @@ -2575,7 +2775,7 @@ kfree(info->toc); if (info->changer_info != NULL) kfree(info->changer_info); - if (devinfo->handle == drive && unregister_cdrom (devinfo)) + if (devinfo->handle == drive && unregister_cdrom(devinfo)) printk ("%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name); kfree(info); drive->driver_data = NULL; @@ -2618,7 +2818,7 @@ MODULE_PARM(ignore, "s"); MODULE_DESCRIPTION("ATAPI CD-ROM Driver"); -void __exit ide_cdrom_exit(void) +static void __exit ide_cdrom_exit(void) { ide_drive_t *drive; int failed = 0; @@ -2661,7 +2861,7 @@ kfree (info); continue; } - memset (info, 0, sizeof (struct cdrom_info)); + memset(info, 0, sizeof (struct cdrom_info)); drive->driver_data = info; DRIVER(drive)->busy++; if (ide_cdrom_setup (drive)) { diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/ide/ide-cd.h linux/drivers/ide/ide-cd.h --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/ide/ide-cd.h Thu May 25 04:54:14 2000 +++ linux/drivers/ide/ide-cd.h Mon Jun 19 02:28:29 2000 @@ -628,7 +628,9 @@ "Logical unit not ready - in progress [sic] of becoming ready" }, { 0x020402, "Logical unit not ready - initializing command required" }, { 0x020403, "Logical unit not ready - manual intervention required" }, - { 0x020404, "In process of becoming ready - writing" }, + { 0x020404, "Logical unit not ready - format in progress" }, + { 0x020407, "Logical unit not ready - operation in progress" }, + { 0x020408, "Logical unit not ready - long write in progress" }, { 0x020600, "No reference position found (media may be upside down)" }, { 0x023000, "Incompatible medium installed" }, { 0x023a00, "Medium not present" }, @@ -678,7 +680,6 @@ { 0x04b600, "Media load mechanism failed" }, { 0x051a00, "Parameter list length error" }, { 0x052000, "Invalid command operation code" }, - { 0x052c00, "Command sequence error" }, { 0x052100, "Logical block address out of range" }, { 0x052102, "Invalid address for write" }, { 0x052400, "Invalid field in command packet" }, diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/ide/ide.c linux/drivers/ide/ide.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/ide/ide.c Tue Jun 20 03:24:41 2000 +++ linux/drivers/ide/ide.c Tue Jun 13 21:19:29 2000 @@ -805,7 +805,7 @@ spin_lock_irqsave(&io_request_lock, flags); blkdev_dequeue_request(rq); HWGROUP(drive)->rq = NULL; - rq->rq_status = RQ_INACTIVE; + blkdev_release_request(rq); spin_unlock_irqrestore(&io_request_lock, flags); if (rq->sem != NULL) up(rq->sem); /* inform originator that rq has been serviced */ diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/scsi.c Tue May 9 00:52:43 2000 +++ linux/drivers/scsi/scsi.c Tue Jun 13 16:24:33 2000 @@ -176,6 +176,8 @@ */ void scsi_initialize_queue(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt) { blk_init_queue(&SDpnt->request_queue, scsi_request_fn); + blk_queue_headactive(&SDpnt->request_queue, 0); + SDpnt->request_queue.queuedata = (void *) SDpnt; } #ifdef MODULE @@ -253,8 +255,10 @@ { Scsi_Request *SRpnt = NULL; - if (!device) - panic("No device passed to scsi_allocate_request().\n"); + if (!device) { + printk("No device passed to scsi_allocate_request().\n"); + BUG(); + } SRpnt = (Scsi_Request *) kmalloc(sizeof(Scsi_Request), GFP_ATOMIC); if( SRpnt == NULL ) @@ -334,8 +338,10 @@ Scsi_Device *SDpnt; unsigned long flags; - if (!device) - panic("No device passed to scsi_allocate_device().\n"); + if (!device) { + printk("No device passed to scsi_allocate_device().\n"); + BUG(); + } host = device->host; @@ -2567,7 +2573,6 @@ } } } - printk("wait_for_request = %p\n", &wait_for_request); #endif /* CONFIG_SCSI_LOGGING */ /* } */ } #endif /* CONFIG_PROC_FS */ @@ -2728,8 +2733,6 @@ scsi_build_commandblocks(SDpnt); scsi_initialize_queue(SDpnt, SHpnt); - blk_queue_headactive(&SDpnt->request_queue, 0); - SDpnt->request_queue.queuedata = (void *) SDpnt; SDpnt->online = TRUE; @@ -2755,9 +2758,9 @@ */ void scsi_free_host_dev(Scsi_Device * SDpnt) { - if( (unsigned char) SDpnt->id != (unsigned char) SDpnt->host->this_id ) - { - panic("Attempt to delete wrong device\n"); + if ((unsigned char) SDpnt->id != (unsigned char) SDpnt->host->this_id) { + printk("Attempt to delete wrong device\n"); + BUG(); } blk_cleanup_queue(&SDpnt->request_queue); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/scsi.h Thu May 25 04:54:21 2000 +++ linux/drivers/scsi/scsi.h Mon Jun 19 02:28:32 2000 @@ -842,6 +842,15 @@ current->state = TASK_RUNNING; \ }; } +/* + * FIXME(eric) - the original disk code disabled clustering for MOD + * devices. I have no idea why we thought this was a good idea - my + * guess is that it was an attempt to limit the size of requests to MOD + * devices. + */ +#define CLUSTERABLE_DEVICE(SH,SD) (SH->use_clustering && \ + SD->type != TYPE_MOD) + #endif /* diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/scsi_dma.c linux/drivers/scsi/scsi_dma.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/scsi_dma.c Sat Mar 11 00:24:02 2000 +++ linux/drivers/scsi/scsi_dma.c Tue Jun 13 16:24:33 2000 @@ -147,8 +147,10 @@ nbits = len >> 9; mask = (1 << nbits) - 1; - if (sector + nbits > SECTORS_PER_PAGE) - panic("scsi_free:Bad memory alignment"); + if (sector + nbits > SECTORS_PER_PAGE) { + printk("scsi_free:Bad memory alignment"); + BUG(); + } if ((dma_malloc_freelist[page] & (mask << sector)) != (mask << sector)) { @@ -156,7 +158,8 @@ printk("scsi_free(obj=%p, len=%d) called from %08lx\n", obj, len, ret); #endif - panic("scsi_free:Trying to free unused memory"); + printk("scsi_free:Trying to free unused memory"); + BUG(); } scsi_dma_free_sectors += nbits; dma_malloc_freelist[page] &= ~(mask << sector); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/scsi_lib.c linux/drivers/scsi/scsi_lib.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/scsi_lib.c Wed Apr 12 18:47:26 2000 +++ linux/drivers/scsi/scsi_lib.c Tue Jun 13 16:24:33 2000 @@ -703,6 +703,11 @@ switch (SCpnt->sense_buffer[2]) { case ILLEGAL_REQUEST: + /* don't requeue a failed WRITE_PACKET request */ + if (SCpnt->request.cmd == WRITE_PACKET) { + SCpnt = scsi_end_request(SCpnt, 0, this_count); + return; + } if (SCpnt->device->ten) { SCpnt->device->ten = 0; /* @@ -855,10 +860,9 @@ * if the device itself is blocked, or if the host is fully * occupied. */ - if (SHpnt->in_recovery - || q->plugged) { + if (SHpnt->in_recovery || q->plugged) return; - } + /* * To start with, we keep looping until the queue is empty, or until * the host is no longer able to accept any more requests. @@ -869,10 +873,8 @@ * released the lock and grabbed it again, so each time * we need to check to see if the queue is plugged or not. */ - if (SHpnt->in_recovery - || q->plugged) { + if (SHpnt->in_recovery || q->plugged) return; - } /* * If the device cannot accept another request, then quit. @@ -884,10 +886,11 @@ || (SHpnt->host_blocked) || (SHpnt->host_self_blocked)) { /* - * If we are unable to process any commands at all for this - * device, then we consider it to be starved. What this means - * is that there are no outstanding commands for this device - * and hence we need a little help getting it started again + * If we are unable to process any commands at all for + * this device, then we consider it to be starved. + * What this means is that there are no outstanding + * commands for this device and hence we need a + * little help getting it started again * once the host isn't quite so busy. */ if (SDpnt->device_busy == 0) { @@ -987,8 +990,8 @@ } /* * If so, we are ready to do something. Bump the count - * while the queue is locked and then break out of the loop. - * Otherwise loop around and try another request. + * while the queue is locked and then break out of the + * loop. Otherwise loop around and try another request. */ if (!SCpnt) { break; @@ -1016,11 +1019,11 @@ memcpy(&SCpnt->request, req, sizeof(struct request)); /* - * We have copied the data out of the request block - it is now in - * a field in SCpnt. Release the request block. + * We have copied the data out of the request block - + * it is now in a field in SCpnt. Release the request + * block. */ - req->rq_status = RQ_INACTIVE; - wake_up(&wait_for_request); + blkdev_release_request(req); } /* * Now it is finally safe to release the lock. We are @@ -1035,12 +1038,14 @@ /* * This will do a couple of things: * 1) Fill in the actual SCSI command. - * 2) Fill in any other upper-level specific fields (timeout). + * 2) Fill in any other upper-level specific fields + * (timeout). * - * If this returns 0, it means that the request failed (reading - * past end of disk, reading offline device, etc). This won't - * actually talk to the device, but some kinds of consistency - * checking may cause the request to be rejected immediately. + * If this returns 0, it means that the request failed + * (reading past end of disk, reading offline device, + * etc). This won't actually talk to the device, but + * some kinds of consistency checking may cause the + * request to be rejected immediately. */ if (STpnt == NULL) { STpnt = scsi_get_request_dev(req); @@ -1083,8 +1088,8 @@ scsi_dispatch_cmd(SCpnt); /* - * Now we need to grab the lock again. We are about to mess with - * the request queue and try to find another command. + * Now we need to grab the lock again. We are about to mess + * with the request queue and try to find another command. */ spin_lock_irq(&io_request_lock); } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/scsi_merge.c linux/drivers/scsi/scsi_merge.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/scsi_merge.c Tue Apr 25 03:59:07 2000 +++ linux/drivers/scsi/scsi_merge.c Tue Jun 13 16:24:33 2000 @@ -76,10 +76,10 @@ * Enable a bunch of additional consistency checking. Turn this off * if you are benchmarking. */ -static int dump_stats(struct request *req, - int use_clustering, - int dma_host, - int segments) +static void dump_stats(struct request *req, + int use_clustering, + int dma_host, + int segments) { struct buffer_head *bh; @@ -89,7 +89,7 @@ */ printk("nr_segments is %x\n", req->nr_segments); printk("counted segments is %x\n", segments); - printk("Flags %d %d\n", use_clustering, dma_host); + printk("Flags %d %d, command %d", use_clustering, dma_host, req->cmd); for (bh = req->bh; bh->b_reqnext != NULL; bh = bh->b_reqnext) { printk("Segment 0x%p, blocks %d, addr 0x%lx\n", @@ -97,7 +97,8 @@ bh->b_size >> 9, virt_to_phys(bh->b_data - 1)); } - panic("Ththththaats all folks. Too dangerous to continue.\n"); + printk("Ththththaats all folks. Too dangerous to continue.\n"); + BUG(); } @@ -151,15 +152,6 @@ } /* - * FIXME(eric) - the original disk code disabled clustering for MOD - * devices. I have no idea why we thought this was a good idea - my - * guess is that it was an attempt to limit the size of requests to MOD - * devices. - */ -#define CLUSTERABLE_DEVICE(SH,SD) (SH->use_clustering && \ - SD->type != TYPE_MOD) - -/* * This entire source file deals with the new queueing code. */ @@ -220,8 +212,8 @@ /* * See if we can do this without creating another * scatter-gather segment. In the event that this is a - * DMA capable host, make sure that a segment doesn't span - * the DMA threshold boundary. + * DMA capable host, make sure that a segment doesn't + * span the DMA threshold boundary. */ if (dma_host && virt_to_phys(bhnext->b_data) - 1 == ISA_DMA_THRESHOLD) { @@ -921,7 +913,7 @@ * Verify that the count is correct. */ if (count != SCpnt->use_sg) { - printk("Incorrect number of segments after building list\n"); + printk("Incorrect number of segments after building list (%u != %u)\n", count, SCpnt->use_sg); #ifdef CONFIG_SCSI_DEBUG_QUEUES dump_stats(req, use_clustering, dma_host, count); #endif @@ -1085,10 +1077,10 @@ * We always force "_VALID" to 1. Eventually clean this up * and get rid of the extra argument. */ -INITIO(scsi_init_io_v, 1, 0, 0) -INITIO(scsi_init_io_vd, 1, 0, 1) -INITIO(scsi_init_io_vc, 1, 1, 0) -INITIO(scsi_init_io_vdc, 1, 1, 1) +INITIO(scsi_init_io_v, 0, 0, 0) +INITIO(scsi_init_io_vd, 0, 0, 1) +INITIO(scsi_init_io_vc, 0, 1, 0) +INITIO(scsi_init_io_vdc, 0, 1, 1) /* * Function: initialize_merge_fn() diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/scsi_scan.c linux/drivers/scsi/scsi_scan.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/scsi_scan.c Tue Jun 20 03:24:43 2000 +++ linux/drivers/scsi/scsi_scan.c Tue Jun 13 15:56:26 2000 @@ -271,8 +271,8 @@ if (SDpnt) { memset(SDpnt, 0, sizeof(Scsi_Device)); /* - * Register the queue for the device. All I/O requests will come - * in through here. We also need to register a pointer to + * Register the queue for the device. All I/O requests will + * come in through here. We also need to register a pointer to * ourselves, since the queue handler won't know what device * the queue actually represents. We could look it up, but it * is pointless work. diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/sr.c linux/drivers/scsi/sr.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/sr.c Tue Jun 20 03:24:43 2000 +++ linux/drivers/scsi/sr.c Tue Jun 13 17:14:53 2000 @@ -28,9 +28,13 @@ * Modified by Jens Axboe - support DVD-RAM * transparently and loose the GHOST hack * + * Modified by Jens Axboe - support packet writing + * through generic packet layer. + * */ #include +#include #include #include @@ -40,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -79,7 +84,7 @@ finish:sr_finish, attach:sr_attach, detach:sr_detach, - init_command:sr_init_command + init_command:sr_init_command, }; Scsi_CD *scsi_CDs = NULL; @@ -109,26 +114,27 @@ static struct cdrom_device_ops sr_dops = { - sr_open, /* open */ - sr_release, /* release */ - sr_drive_status, /* drive status */ - sr_media_change, /* media changed */ - sr_tray_move, /* tray move */ - sr_lock_door, /* lock door */ - sr_select_speed, /* select speed */ - NULL, /* select disc */ - sr_get_last_session, /* get last session */ - sr_get_mcn, /* get universal product code */ - sr_reset, /* hard reset */ - sr_audio_ioctl, /* audio ioctl */ - sr_dev_ioctl, /* device-specific ioctl */ - CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | - CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | - CDC_PLAY_AUDIO | CDC_RESET | CDC_IOCTLS | CDC_DRIVE_STATUS | - CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | - CDC_GENERIC_PACKET, - 0, - sr_packet + open: sr_open, + release: sr_release, + drive_status: sr_drive_status, + media_changed: sr_media_change, + tray_move: sr_tray_move, + lock_door: sr_lock_door, + select_speed: sr_select_speed, + get_last_session: sr_get_last_session, + get_mcn: sr_get_mcn, + reset: sr_reset, + audio_ioctl: sr_audio_ioctl, + dev_ioctl: sr_dev_ioctl, + capability: CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | + CDC_SELECT_SPEED | CDC_SELECT_DISC | + CDC_MULTI_SESSION | CDC_MCN | + CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | + CDC_IOCTLS | CDC_DRIVE_STATUS | CDC_CD_R | + CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | + CDC_GENERIC_PACKET, + n_minors: 0, + generic_packet: sr_packet }; /* @@ -186,8 +192,9 @@ } /* - * rw_intr is the interrupt routine for the device driver. It will be notified on the - * end of a SCSI read / write, and will take on of several actions based on success or failure. + * rw_intr is the interrupt routine for the device driver. It will be notified + * on the end of a SCSI read / write, and will take on of several actions + * based on success or failure. */ static void rw_intr(Scsi_Cmnd * SCpnt) @@ -197,13 +204,15 @@ int good_sectors = (result == 0 ? this_count : 0); int block_sectors = 0; -#ifdef DEBUG - printk("sr.c done: %x %x\n", result, SCpnt->request.bh->b_data); -#endif + if (SCpnt->request.cmd == WRITE_PACKET) { + printk("sr.c done: %x %p %lu %lu\n", result, SCpnt->request.bh->b_data, SCpnt->request.sector, SCpnt->request.nr_sectors); + } + /* - Handle MEDIUM ERRORs or VOLUME OVERFLOWs that indicate partial success. - Since this is a relatively rare error condition, no care is taken to - avoid unnecessary additional work such as memcpy's that could be avoided. + * Handle MEDIUM ERRORs or VOLUME OVERFLOWs that indicate partial + * success. Since this is a relatively rare error condition, no care + * is taken to avoid unnecessary additional work such as memcpy's that + * could be avoided. */ @@ -237,6 +246,7 @@ scsi_CDs[device_nr].capacity - error_sector < 4 * 75) sr_sizes[device_nr] = error_sector >> 1; } + /* * This calls the generic completion function, now that we know * how many actual sectors finished, and how many sectors we need @@ -245,7 +255,6 @@ scsi_io_completion(SCpnt, good_sectors, block_sectors); } - static request_queue_t *sr_find_queue(kdev_t dev) { /* @@ -259,7 +268,8 @@ static int sr_init_command(Scsi_Cmnd * SCpnt) { - int dev, devm, block, this_count; + int dev, devm, this_count; + unsigned long block; devm = MINOR(SCpnt->request.rq_dev); dev = DEVICE_NR(SCpnt->request.rq_dev); @@ -284,8 +294,8 @@ } if (scsi_CDs[dev].device->changed) { /* - * quietly refuse to do anything to a changed disc until the changed - * bit has been reset + * quietly refuse to do anything to a changed disc until + * the changed bit has been reset */ /* printk("SCSI disk has been changed. Prohibiting further I/O.\n"); */ return 0; @@ -306,7 +316,7 @@ if (scsi_CDs[dev].device->sector_size == 1024) { if ((block & 1) || (SCpnt->request.nr_sectors & 1)) { - printk("sr.c:Bad 1K block number requested (%d %ld)", + printk("sr.c:Bad 1K block number requested (%lu %ld)", block, SCpnt->request.nr_sectors); return 0; } else { @@ -316,7 +326,7 @@ } if (scsi_CDs[dev].device->sector_size == 2048) { if ((block & 3) || (SCpnt->request.nr_sectors & 3)) { - printk("sr.c:Bad 2K block number requested (%d %ld)", + printk("sr.c:Bad 2K block number requested (%ld %ld)", block, SCpnt->request.nr_sectors); return 0; } else { @@ -325,6 +335,11 @@ } } switch (SCpnt->request.cmd) { + case WRITE_PACKET: + printk("sr: got WRITE_PACKET\n"); + SCpnt->cmnd[0] = WRITE_10; + SCpnt->sc_data_direction = SCSI_DATA_WRITE; + break; case WRITE: SCpnt->cmnd[0] = WRITE_10; SCpnt->sc_data_direction = SCSI_DATA_WRITE; @@ -468,23 +483,24 @@ int sector_size; Scsi_Request *SRpnt; - buffer = (unsigned char *) scsi_malloc(512); - + if ((buffer = (unsigned char *) scsi_malloc(512)) == NULL) + return; - SRpnt = scsi_allocate_request(scsi_CDs[i].device); + if ((SRpnt = scsi_allocate_request(scsi_CDs[i].device)) == NULL) + return; retries = 3; do { cmd[0] = READ_CAPACITY; cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0; memset((void *) &cmd[2], 0, 8); - SRpnt->sr_request.rq_status = RQ_SCSI_BUSY; /* Mark as really busy */ + /* Mark as really busy */ + SRpnt->sr_request.rq_status = RQ_SCSI_BUSY; SRpnt->sr_cmd_len = 0; memset(buffer, 0, 8); /* Do the command and wait.. */ - SRpnt->sr_data_direction = SCSI_DATA_READ; scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer, 8, SR_TIMEOUT, MAX_RETRIES); @@ -500,17 +516,17 @@ if (the_result) { scsi_CDs[i].capacity = 0x1fffff; - sector_size = 2048; /* A guess, just in case */ + /* A guess, just in case */ + sector_size = 2048; scsi_CDs[i].needs_sector_size = 1; } else { -#if 0 if (cdrom_get_last_written(MKDEV(MAJOR_NR, i), - (long *) &scsi_CDs[i].capacity)) -#endif + (long *) &scsi_CDs[i].capacity)) { scsi_CDs[i].capacity = 1 + ((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); + } sector_size = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; switch (sector_size) { @@ -646,10 +662,8 @@ unsigned char *buffer = cgc->buffer; int buflen; - /* get the device */ - SRpnt = scsi_allocate_request(device); - if (SRpnt == NULL) - return -ENODEV; /* this just doesn't seem right /axboe */ + if ((SRpnt = scsi_allocate_request(device)) == NULL) + return -ENOMEM; /* use buffer for ISA DMA */ buflen = (cgc->buflen + 511) & ~511; @@ -672,10 +686,10 @@ SRpnt->sr_data_direction = cgc->data_direction; scsi_wait_req(SRpnt, (void *) cgc->cmd, (void *) buffer, cgc->buflen, - SR_TIMEOUT, MAX_RETRIES); + (60 * 60 * HZ), MAX_RETRIES); - if ((cgc->stat = SRpnt->sr_result)) - cgc->sense = (struct request_sense *) SRpnt->sr_sense_buffer; + cgc->stat = SRpnt->sr_result; + cgc->sense = (struct request_sense *) SRpnt->sr_sense_buffer; /* release */ SRpnt->sr_request.rq_dev = MKDEV(0, 0); @@ -688,7 +702,6 @@ scsi_free(buffer, buflen); } - return cgc->stat; } @@ -703,7 +716,7 @@ if (!sr_registered) { if (devfs_register_blkdev(MAJOR_NR, "sr", &cdrom_fops)) { - printk("Unable to get major %d for SCSI-CD\n", MAJOR_NR); + printk("Unable to get major %d for SCSI-CD\n",MAJOR_NR); return 1; } sr_registered++; @@ -740,6 +753,7 @@ { int i; char name[6]; + struct cdrom_device_info *cdi; blk_dev[MAJOR_NR].queue = sr_find_queue; blk_size[MAJOR_NR] = sr_sizes; @@ -750,9 +764,11 @@ if (scsi_CDs[i].capacity) continue; scsi_CDs[i].capacity = 0x1fffff; - scsi_CDs[i].device->sector_size = 2048; /* A guess, just in case */ + /* A guess, just in case */ + scsi_CDs[i].device->sector_size = 2048; scsi_CDs[i].needs_sector_size = 1; - scsi_CDs[i].device->changed = 1; /* force recheck CD type */ + /* force recheck CD type */ + scsi_CDs[i].device->changed = 1; #if 0 /* seems better to leave this for later */ get_sectorsize(i); @@ -766,34 +782,35 @@ scsi_CDs[i].readcd_cdda = 0; sr_sizes[i] = scsi_CDs[i].capacity >> (BLOCK_SIZE_BITS - 9); - scsi_CDs[i].cdi.ops = &sr_dops; - scsi_CDs[i].cdi.handle = &scsi_CDs[i]; - scsi_CDs[i].cdi.dev = MKDEV(MAJOR_NR, i); - scsi_CDs[i].cdi.mask = 0; - scsi_CDs[i].cdi.capacity = 1; + cdi = &scsi_CDs[i].cdi; + cdi->ops = &sr_dops; + cdi->handle = &scsi_CDs[i]; + cdi->dev = MKDEV(MAJOR_NR, i); + cdi->mask = 0; + cdi->capacity = 1; get_capabilities(i); sr_vendor_init(i); sprintf(name, "sr%d", i); - strcpy(scsi_CDs[i].cdi.name, name); - scsi_CDs[i].cdi.de = - devfs_register (scsi_CDs[i].device->de, "cd", 2, - DEVFS_FL_DEFAULT, MAJOR_NR, i, - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, - &cdrom_fops, NULL); - register_cdrom(&scsi_CDs[i].cdi); + strcpy(cdi->name, name); + cdi->de = devfs_register(scsi_CDs[i].device->de, "cd", 2, + DEVFS_FL_DEFAULT, MAJOR_NR, i, + S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, + &cdrom_fops, NULL); + register_cdrom(cdi); } /* If our host adapter is capable of scatter-gather, then we increase * the read-ahead to 16 blocks (32 sectors). If not, we use * a two block (4 sector) read ahead. */ - if (scsi_CDs[0].device && scsi_CDs[0].device->host->sg_tablesize) - read_ahead[MAJOR_NR] = 32; /* 32 sector read-ahead. Always removable. */ - else - read_ahead[MAJOR_NR] = 4; /* 4 sector read-ahead */ - - return; + if (scsi_CDs[0].device && scsi_CDs[0].device->host->sg_tablesize) { + /* 32 sector read-ahead. Always removable. */ + read_ahead[MAJOR_NR] = 32; + } else { + /* 4 sector read-ahead */ + read_ahead[MAJOR_NR] = 4; + } } static void sr_detach(Scsi_Device * SDp) @@ -805,20 +822,23 @@ if (cpnt->device == SDp) { kdev_t devi = MKDEV(MAJOR_NR, i); struct super_block *sb = get_super(devi); + struct cdrom_device_info *cdi; /* - * Since the cdrom is read-only, no need to sync the device. - * We should be kind to our buffer cache, however. + * Since the cdrom is read-only, no need to sync the + * device. We should be kind to our buffer cache, + * however. */ if (sb) invalidate_inodes(sb); invalidate_buffers(devi); /* - * Reset things back to a sane state so that one can re-load a new - * driver (perhaps the same one). + * Reset things back to a sane state so that one can + * re-load a new driver (perhaps the same one). */ - unregister_cdrom(&(cpnt->cdi)); + cdi = &(cpnt->cdi); + unregister_cdrom(cdi); cpnt->device = NULL; cpnt->capacity = 0; SDp->attached--; @@ -827,19 +847,15 @@ sr_sizes[i] = 0; return; } - return; } - -#ifdef MODULE - -int init_module(void) +static int __init sr_module_init(void) { - sr_template.module = &__this_module; + sr_template.module = THIS_MODULE; return scsi_register_module(MODULE_SCSI_DEV, &sr_template); } -void cleanup_module(void) +static void __exit sr_module_exit(void) { scsi_unregister_module(MODULE_SCSI_DEV, &sr_template); devfs_unregister_blkdev(MAJOR_NR, "sr"); @@ -857,30 +873,11 @@ } blksize_size[MAJOR_NR] = NULL; hardsect_size[MAJOR_NR] = NULL; - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); blk_size[MAJOR_NR] = NULL; read_ahead[MAJOR_NR] = 0; sr_template.dev_max = 0; } -#endif /* MODULE */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 4 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -4 - * c-argdecl-indent: 4 - * c-label-offset: -4 - * c-continued-statement-offset: 4 - * c-continued-brace-offset: 0 - * indent-tabs-mode: nil - * tab-width: 8 - * End: - */ +module_init(sr_module_init); +module_exit(sr_module_exit); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c --- /opt/kernel/linux-2.4.0-test1-ac21/drivers/scsi/sr_ioctl.c Wed Feb 23 07:19:07 2000 +++ linux/drivers/scsi/sr_ioctl.c Tue Jun 13 16:24:33 2000 @@ -43,7 +43,8 @@ char *bounce_buffer; SDev = scsi_CDs[target].device; - SRpnt = scsi_allocate_request(scsi_CDs[target].device); + if ((SRpnt = scsi_allocate_request(scsi_CDs[target].device)) == NULL) + return -ENOMEM; SRpnt->sr_data_direction = readwrite; /* use ISA DMA buffer if necessary */ diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/fs/buffer.c linux/fs/buffer.c --- /opt/kernel/linux-2.4.0-test1-ac21/fs/buffer.c Tue Jun 20 03:24:44 2000 +++ linux/fs/buffer.c Sat Jun 17 17:55:14 2000 @@ -765,7 +765,7 @@ BUG(); } -static void end_buffer_io_async(struct buffer_head * bh, int uptodate) +void end_buffer_io_async(struct buffer_head * bh, int uptodate) { static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED; unsigned long flags; @@ -2523,7 +2523,7 @@ /* The buffer cache hash table is less important these days, * trim it a bit. */ - mempages >>= 14; + mempages >>= 12; mempages *= sizeof(struct buffer_head *); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/fs/udf/fsync.c linux/fs/udf/fsync.c --- /opt/kernel/linux-2.4.0-test1-ac21/fs/udf/fsync.c Tue Jun 20 03:24:46 2000 +++ linux/fs/udf/fsync.c Tue Jun 13 16:24:33 2000 @@ -29,6 +29,7 @@ #include #include #include +#include #include "udf_i.h" static int sync_extent_block (struct inode * inode, Uint32 block, int wait) diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/asm-i386/hw_irq.h linux/include/asm-i386/hw_irq.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/asm-i386/hw_irq.h Thu May 25 04:52:41 2000 +++ linux/include/asm-i386/hw_irq.h Tue Jun 13 16:30:11 2000 @@ -22,6 +22,7 @@ #define FIRST_EXTERNAL_VECTOR 0x20 #define SYSCALL_VECTOR 0x80 +#define KDBENTER_VECTOR 0x81 /* * Vectors 0x20-0x2f are used for ISA interrupts. @@ -41,6 +42,7 @@ #define INVALIDATE_TLB_VECTOR 0xfd #define RESCHEDULE_VECTOR 0xfc #define CALL_FUNCTION_VECTOR 0xfb +#define KDB_VECTOR 0xfa /* * Local APIC timer IRQ vector is on a different priority level, diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/asm-i386/kdb.h linux/include/asm-i386/kdb.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/asm-i386/kdb.h Thu Jan 1 01:00:00 1970 +++ linux/include/asm-i386/kdb.h Tue Jun 13 16:30:11 2000 @@ -0,0 +1,48 @@ +/* + * Minimalist Kernel Debugger + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + */ +#if !defined(_ASM_KDB_H) +#define _ASM_KDB_H + + /* + * KDB_ENTER() is a macro which causes entry into the kernel + * debugger from any point in the kernel code stream. If it + * is intended to be used from interrupt level, it must use + * a non-maskable entry method. + */ +#define KDB_ENTER() asm("\tint $129\n") + + /* + * Define the exception frame for this architeture + */ +struct pt_regs; +typedef struct pt_regs *kdb_eframe_t; + + /* + * Define SMP architecture state + */ +extern volatile unsigned long smp_kdb_wait; + + /* + * Needed for exported symbols. + */ +typedef unsigned long kdb_machreg_t; + +#endif /* ASM_KDB_H */ diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/asm-i386/kdbprivate.h linux/include/asm-i386/kdbprivate.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/asm-i386/kdbprivate.h Thu Jan 1 01:00:00 1970 +++ linux/include/asm-i386/kdbprivate.h Tue Jun 13 16:30:11 2000 @@ -0,0 +1,176 @@ +/* + * Minimalist Kernel Debugger + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ +#if !defined(_ASM_KDBPRIVATE_H) +#define _ASM_KDBPRIVATE_H + +typedef unsigned char kdb_machinst_t; + + /* + * KDB_MAXBPT describes the total number of breakpoints + * supported by this architecure. + */ +#define KDB_MAXBPT 16 + /* + * KDB_MAXHARDBPT describes the total number of hardware + * breakpoint registers that exist. + */ +#define KDB_MAXHARDBPT 4 + /* + * Provide space for KDB_MAX_COMMANDS commands. + */ +#define KDB_MAX_COMMANDS 125 + + /* + * Platform specific environment entries + */ +#define KDB_PLATFORM_ENV "IDMODE=x86", "BYTESPERWORD=4" + + /* + * Define the direction that the stack grows + */ +#define KDB_STACK_DIRECTION -1 /* Stack grows down */ + + /* + * Support for ia32 debug registers + */ +typedef struct _kdbhard_bp { + kdb_machreg_t bph_reg; /* Register this breakpoint uses */ + + unsigned int bph_free:1; /* Register available for use */ + unsigned int bph_data:1; /* Data Access breakpoint */ + + unsigned int bph_write:1; /* Write Data breakpoint */ + unsigned int bph_mode:2; /* 0=inst, 1=write, 2=io, 3=read */ + unsigned int bph_length:2; /* 0=1, 1=2, 2=BAD, 3=4 (bytes) */ +} kdbhard_bp_t; + +extern kdbhard_bp_t kdb_hardbreaks[/* KDB_MAXHARDBPT */]; + +#define IA32_BREAKPOINT_INSTRUCTION 0xcc + +#define DR6_BT 0x00008000 +#define DR6_BS 0x00004000 +#define DR6_BD 0x00002000 + +#define DR6_B3 0x00000008 +#define DR6_B2 0x00000004 +#define DR6_B1 0x00000002 +#define DR6_B0 0x00000001 + +#define DR7_RW_VAL(dr, drnum) \ + (((dr) >> (16 + (4 * (drnum)))) & 0x3) + +#define DR7_RW_SET(dr, drnum, rw) \ + do { \ + (dr) &= ~(0x3 << (16 + (4 * (drnum)))); \ + (dr) |= (((rw) & 0x3) << (16 + (4 * (drnum)))); \ + } while (0) + +#define DR7_RW0(dr) DR7_RW_VAL(dr, 0) +#define DR7_RW0SET(dr,rw) DR7_RW_SET(dr, 0, rw) +#define DR7_RW1(dr) DR7_RW_VAL(dr, 1) +#define DR7_RW1SET(dr,rw) DR7_RW_SET(dr, 1, rw) +#define DR7_RW2(dr) DR7_RW_VAL(dr, 2) +#define DR7_RW2SET(dr,rw) DR7_RW_SET(dr, 2, rw) +#define DR7_RW3(dr) DR7_RW_VAL(dr, 3) +#define DR7_RW3SET(dr,rw) DR7_RW_SET(dr, 3, rw) + + +#define DR7_LEN_VAL(dr, drnum) \ + (((dr) >> (18 + (4 * (drnum)))) & 0x3) + +#define DR7_LEN_SET(dr, drnum, rw) \ + do { \ + (dr) &= ~(0x3 << (18 + (4 * (drnum)))); \ + (dr) |= (((rw) & 0x3) << (18 + (4 * (drnum)))); \ + } while (0) +#define DR7_LEN0(dr) DR7_LEN_VAL(dr, 0) +#define DR7_LEN0SET(dr,len) DR7_LEN_SET(dr, 0, len) +#define DR7_LEN1(dr) DR7_LEN_VAL(dr, 1) +#define DR7_LEN1SET(dr,len) DR7_LEN_SET(dr, 1, len) +#define DR7_LEN2(dr) DR7_LEN_VAL(dr, 2) +#define DR7_LEN2SET(dr,len) DR7_LEN_SET(dr, 2, len) +#define DR7_LEN3(dr) DR7_LEN_VAL(dr, 3) +#define DR7_LEN3SET(dr,len) DR7_LEN_SET(dr, 3, len) + +#define DR7_G0(dr) (((dr)>>1)&0x1) +#define DR7_G0SET(dr) ((dr) |= 0x2) +#define DR7_G0CLR(dr) ((dr) &= ~0x2) +#define DR7_G1(dr) (((dr)>>3)&0x1) +#define DR7_G1SET(dr) ((dr) |= 0x8) +#define DR7_G1CLR(dr) ((dr) &= ~0x8) +#define DR7_G2(dr) (((dr)>>5)&0x1) +#define DR7_G2SET(dr) ((dr) |= 0x20) +#define DR7_G2CLR(dr) ((dr) &= ~0x20) +#define DR7_G3(dr) (((dr)>>7)&0x1) +#define DR7_G3SET(dr) ((dr) |= 0x80) +#define DR7_G3CLR(dr) ((dr) &= ~0x80) + +#define DR7_L0(dr) (((dr))&0x1) +#define DR7_L0SET(dr) ((dr) |= 0x1) +#define DR7_L0CLR(dr) ((dr) &= ~0x1) +#define DR7_L1(dr) (((dr)>>2)&0x1) +#define DR7_L1SET(dr) ((dr) |= 0x4) +#define DR7_L1CLR(dr) ((dr) &= ~0x4) +#define DR7_L2(dr) (((dr)>>4)&0x1) +#define DR7_L2SET(dr) ((dr) |= 0x10) +#define DR7_L2CLR(dr) ((dr) &= ~0x10) +#define DR7_L3(dr) (((dr)>>6)&0x1) +#define DR7_L3SET(dr) ((dr) |= 0x40) +#define DR7_L3CLR(dr) ((dr) &= ~0x40) + +#define DR7_GD 0x00002000 /* General Detect Enable */ +#define DR7_GE 0x00000200 /* Global exact */ +#define DR7_LE 0x00000100 /* Local exact */ + +extern kdb_machreg_t kdb_getdr6(void); +extern void kdb_putdr6(kdb_machreg_t); + +extern kdb_machreg_t kdb_getdr7(void); +extern void kdb_putdr7(kdb_machreg_t); + +extern kdb_machreg_t kdb_getdr(int); +extern void kdb_putdr(int, kdb_machreg_t); + +extern kdb_machreg_t kdb_getcr(int); + +/* + * 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 /* !_ASM_KDBPRIVATE_H */ diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/asm-i386/keyboard.h linux/include/asm-i386/keyboard.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/asm-i386/keyboard.h Thu May 25 04:53:50 2000 +++ linux/include/asm-i386/keyboard.h Mon Jun 19 04:21:16 2000 @@ -38,6 +38,7 @@ #define kbd_sysrq_xlate pckbd_sysrq_xlate #define SYSRQ_KEY 0x54 +#define E1_PAUSE 119 /* PAUSE key */ /* resource allocation */ #define kbd_request_region() diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/asm-i386/msr.h linux/include/asm-i386/msr.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/asm-i386/msr.h Thu Oct 7 19:17:09 1999 +++ linux/include/asm-i386/msr.h Tue Jun 13 16:30:11 2000 @@ -30,3 +30,17 @@ : "=a" (low), "=d" (high) \ : "c" (counter)) +#define MCG_STATUS_MSR 0x17a +#define MCG_CAP 0x179 +#define MCG_CTL 0x17b +#define MC0_BASE 0x400 +#define MC0_CTL_OFFSET 0x0 +#define MC0_STATUS_OFFSET 0x1 +#define MC0_ADDR_OFFSET 0x2 +#define MC0_MISC_OFFSET 0x3 +#define MC0_BANK_COUNT 0x4 +#define DEBUGCTLMSR 0x1d9 +#define LASTBRANCHFROMIP 0x1db +#define LASTBRANCHTOIP 0x1dc +#define LASTINTFROMIP 0x1dd +#define LASTINTTOIP 0x1de diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/asm-i386/ptrace.h linux/include/asm-i386/ptrace.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/asm-i386/ptrace.h Tue Jun 20 03:24:46 2000 +++ linux/include/asm-i386/ptrace.h Tue Jun 13 16:30:11 2000 @@ -49,6 +49,29 @@ #define PTRACE_GETXFPREGS 25 #define PTRACE_SETXFPREGS 26 +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 --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/linux/blk.h linux/include/linux/blk.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/linux/blk.h Thu May 25 04:54:12 2000 +++ linux/include/linux/blk.h Mon Jun 19 02:27:44 2000 @@ -14,13 +14,6 @@ extern spinlock_t io_request_lock; /* - * NR_REQUEST is the number of entries in the request-queue. - * NOTE that writes may use only the low 2/3 of these: reads - * take precedence. - */ -#define NR_REQUEST 256 - -/* * Initialization functions. */ extern int isp16_init(void); @@ -94,12 +87,9 @@ extern inline void blkdev_dequeue_request(struct request * req) { - if (req->q) - { - if (req->cmd == READ) - req->q->elevator.read_pendings--; - req->q->elevator.nr_segments -= req->nr_segments; - req->q = NULL; + if (req->e) { + req->e->dequeue_fn(req); + req->e = NULL; } list_del(&req->queue); } diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/linux/blkdev.h linux/include/linux/blkdev.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/linux/blkdev.h Thu May 25 04:53:17 2000 +++ linux/include/linux/blkdev.h Mon Jun 19 02:27:44 2000 @@ -6,9 +6,12 @@ #include #include #include +#include struct request_queue; typedef struct request_queue request_queue_t; +struct elevator_s; +typedef struct elevator_s elevator_t; /* * Ok, this is an expanded form so that we can use the same @@ -19,7 +22,11 @@ struct request { struct list_head queue; int elevator_sequence; + struct list_head table; + /* + * queue free list belongs to + */ volatile int rq_status; /* should split this into a few status bits */ #define RQ_INACTIVE (-1) #define RQ_ACTIVE 1 @@ -41,7 +48,8 @@ struct semaphore * sem; struct buffer_head * bh; struct buffer_head * bhtail; - request_queue_t * q; + request_queue_t *q; + elevator_t *e; }; #include @@ -60,15 +68,30 @@ typedef void (plug_device_fn) (request_queue_t *q, kdev_t device); typedef void (unplug_device_fn) (void *q); +/* + * Default nr free requests per queue + */ +#define QUEUE_NR_REQUESTS 512 +#define QUEUE_WRITES_MAX ((2 * QUEUE_NR_REQUESTS) / 3) + struct request_queue { - struct list_head queue_head; - /* together with queue_head for cacheline sharing */ - elevator_t elevator; + /* + * the queue request freelist, one for reads and one for writes + */ + struct list_head request_freelist; + int queue_requests; + + /* + * Together with queue_head for cacheline sharing + */ + struct list_head queue_head; + elevator_t elevator; request_fn_proc * request_fn; merge_request_fn * back_merge_fn; merge_request_fn * front_merge_fn; + merge_request_fn * hole_merge_fn; merge_requests_fn * merge_requests_fn; make_request_fn * make_request_fn; plug_device_fn * plug_device_fn; @@ -76,22 +99,31 @@ * The queue owner gets to use this for whatever they like. * ll_rw_blk doesn't touch it. */ - void * queuedata; + void * queuedata; + + struct tq_struct plug_tq; - /* - * This is used to remove the plug when tq_disk runs. - */ - struct tq_struct plug_tq; /* * Boolean that indicates whether this queue is plugged or not. */ - char plugged; + char plugged; /* * Boolean that indicates whether current_request is active or * not. */ - char head_active; + char head_active; + + /* + * Is meant to protect the queue in the future instead of + * io_request_lock + */ + spinlock_t request_lock; + + /* + * Tasks wait here for free request + */ + wait_queue_head_t wait_for_request; }; struct blk_dev_struct { @@ -118,13 +150,13 @@ extern struct sec_size * blk_sec[MAX_BLKDEV]; extern struct blk_dev_struct blk_dev[MAX_BLKDEV]; -extern wait_queue_head_t wait_for_request; extern void grok_partitions(struct gendisk *dev, int drive, unsigned minors, long size); extern void register_disk(struct gendisk *dev, kdev_t first, unsigned minors, struct block_device_operations *ops, long size); -extern void generic_unplug_device(void * data); +extern void generic_unplug_device(void *data); extern int generic_make_request(request_queue_t *q, int rw, struct buffer_head * bh); -extern request_queue_t * blk_get_queue(kdev_t dev); +extern request_queue_t *blk_get_queue(kdev_t dev); +extern void blkdev_release_request(struct request *); /* * Access functions for manipulating queue properties diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/linux/cdrom.h linux/include/linux/cdrom.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/linux/cdrom.h Tue Jun 20 03:24:47 2000 +++ linux/include/linux/cdrom.h Mon Jun 19 02:27:48 2000 @@ -497,7 +497,10 @@ * of MODE_SENSE_POWER_PAGE */ #define GPMODE_CDROM_PAGE 0x0d - +#define GPMODE_PAGE_CURRENT 0 +#define GPMODE_PAGE_CHANGE 1 +#define GPMODE_PAGE_DEFAULT 2 +#define GPMODE_PAGE_SAVE 3 /* DVD struct types */ #define DVD_STRUCT_PHYSICAL 0x00 diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/linux/dis-asm.h linux/include/linux/dis-asm.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/linux/dis-asm.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/dis-asm.h Tue Jun 13 16:30:11 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 --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/linux/elevator.h linux/include/linux/elevator.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/linux/elevator.h Tue Jun 20 03:24:47 2000 +++ linux/include/linux/elevator.h Tue Jun 13 17:06:02 2000 @@ -3,13 +3,15 @@ #define ELEVATOR_DEBUG -struct elevator_s; -typedef struct elevator_s elevator_t; - typedef void (elevator_fn) (struct request *, elevator_t *, struct list_head *, struct list_head *, int); +typedef int (elevator_merge_fn) (request_queue_t *, struct request **, + struct buffer_head *, int, int *, int *); + +typedef void (elevator_dequeue_fn) (struct request *); + struct elevator_s { int sequence; @@ -21,31 +23,28 @@ unsigned int nr_segments; int read_pendings; + char elevator_name[16]; + elevator_fn * elevator_fn; + elevator_merge_fn *elevator_merge_fn; + elevator_dequeue_fn *dequeue_fn; unsigned int queue_ID; }; -#define ELEVATOR_DEFAULTS \ -((elevator_t) { \ - 0, /* sequence */ \ - \ - 128, /* read_latency */ \ - 8192, /* write_latency */ \ - 32, /* max_bomb_segments */ \ - \ - 0, /* nr_segments */ \ - 0, /* read_pendings */ \ - \ - elevator_default, /* elevator_fn */ \ - }) - +void elevator_default(struct request *, elevator_t *, struct list_head *, struct list_head *, int); +int elevator_default_merge(request_queue_t *, struct request **, struct buffer_head *, int, int *, int *); +void elevator_default_dequeue(struct request *); +void elevator_noop(struct request *, elevator_t *, struct list_head *, struct list_head *, int); +int elevator_noop_merge(request_queue_t *, struct request **, struct buffer_head *, int, int *, int *); +void elevator_noop_dequeue(struct request *); typedef struct blkelv_ioctl_arg_s { int queue_ID; int read_latency; int write_latency; int max_bomb_segments; + char elevator_name[16]; } blkelv_ioctl_arg_t; #define BLKELVGET _IOR(0x12,106,sizeof(blkelv_ioctl_arg_t)) @@ -54,13 +53,12 @@ extern int blkelvget_ioctl(elevator_t *, blkelv_ioctl_arg_t *); extern int blkelvset_ioctl(elevator_t *, const blkelv_ioctl_arg_t *); - -extern void elevator_init(elevator_t *); +extern void elevator_init(elevator_t *, elevator_t); #ifdef ELEVATOR_DEBUG -extern void elevator_debug(request_queue_t *, kdev_t); +extern void elevator_default_debug(request_queue_t *, kdev_t); #else -#define elevator_debug(a,b) do { } while(0) +#define elevator_default_debug(a,b) do { } while(0) #endif #define elevator_sequence_after(a,b) ((int)((b)-(a)) < 0) @@ -69,6 +67,14 @@ #define elevator_sequence_before_eq(a,b) elevator_sequence_after_eq(b,a) /* + * Return values from elevator merger + */ +#define ELEVATOR_NO_MERGE 0 +#define ELEVATOR_FRONT_MERGE 1 +#define ELEVATOR_BACK_MERGE 2 +#define ELEVATOR_HOLE_MERGE 3 + +/* * This is used in the elevator algorithm. We don't prioritise reads * over writes any more --- although reads are more time-critical than * writes, by treating them equally we increase filesystem throughput. @@ -79,12 +85,12 @@ (s1)->sector < (s2)->sector)) || \ (s1)->rq_dev < (s2)->rq_dev) -static inline void elevator_merge_requests(elevator_t * e, struct request * req, struct request * next) +static inline void elevator_merge_requests(struct request * req, struct request * next) { if (elevator_sequence_before(next->elevator_sequence, req->elevator_sequence)) req->elevator_sequence = next->elevator_sequence; if (req->cmd == READ) - e->read_pendings--; + req->e->read_pendings--; } @@ -93,23 +99,23 @@ return latency + e->sequence; } -#define elevator_merge_before(q, req, lat) __elevator_merge((q), (req), (lat), 0) -#define elevator_merge_after(q, req, lat) __elevator_merge((q), (req), (lat), 1) -static inline void __elevator_merge(elevator_t * elevator, struct request * req, int latency, int after) +#define elevator_merge_before(req, lat) __elevator_merge((req), (lat), 0) +#define elevator_merge_after(req, lat) __elevator_merge((req), (lat), 1) +static inline void __elevator_merge(struct request * req, int latency, int after) { - int sequence = elevator_sequence(elevator, latency); + int sequence = elevator_sequence(req->e, latency); if (after) sequence -= req->nr_segments; if (elevator_sequence_before(sequence, req->elevator_sequence)) req->elevator_sequence = sequence; } -static inline void elevator_account_request(elevator_t * elevator, struct request * req) +static inline void elevator_account_request(struct request * req) { - elevator->sequence++; + req->e->sequence++; if (req->cmd == READ) - elevator->read_pendings++; - elevator->nr_segments++; + req->e->read_pendings++; + req->e->nr_segments++; } static inline int elevator_request_latency(elevator_t * elevator, int rw) @@ -122,5 +128,41 @@ return latency; } + +#define ELEVATOR_DEFAULT \ +((elevator_t) { \ + 0, /* sequence */ \ + \ + 100000, /* read_latency */ \ + 100000, /* write_latency */ \ + 128, /* max_bomb_segments */ \ + \ + 0, /* nr_segments */ \ + 0, /* read_pendings */ \ + \ + "default", /* elevator_name */ \ + \ + elevator_default, /* elevator_fn */ \ + elevator_default_merge, /* elevator_merge_fn */ \ + elevator_default_dequeue, /* dequeue_fn */ \ + }) + +#define ELEVATOR_NOOP \ +((elevator_t) { \ + 0, /* sequence */ \ + \ + 0, /* read_latency */ \ + 0, /* write_latency */ \ + 0, /* max_bomb_segments */ \ + \ + 0, /* nr_segments */ \ + 0, /* read_pendings */ \ + \ + "noop", /* elevator_name */ \ + \ + elevator_noop, /* elevator_fn */ \ + elevator_noop_merge, /* elevator_merge_fn */ \ + elevator_noop_dequeue, /* dequeue_fn */ \ + }) #endif diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/linux/fs.h linux/include/linux/fs.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/linux/fs.h Tue Jun 20 03:24:47 2000 +++ linux/include/linux/fs.h Mon Jun 19 02:27:44 2000 @@ -70,6 +70,7 @@ #define WRITE 1 #define READA 2 /* read-ahead - don't block if no resources */ #define SPECIAL 4 /* For non-blockdevice requests in request queue */ +#define WRITE_PACKET 5 /* for packet writers */ #define WRITERAW 5 /* raw write - don't play with buffer lists */ @@ -779,6 +780,7 @@ int (*remount_fs) (struct super_block *, int *, char *); void (*clear_inode) (struct inode *); void (*umount_begin) (struct super_block *); + int (*block_empty) (struct super_block *, int, unsigned long *); }; struct dquot_operations { diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/linux/kallsyms.h linux/include/linux/kallsyms.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/linux/kallsyms.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/kallsyms.h Tue Jun 13 16:33:01 2000 @@ -0,0 +1,131 @@ +/* kallsyms headers + Copyright 2000 Keith Owens + + This file is part of the Linux modutils. It is exported to kernel + space so debuggers can access the kallsyms data. + + The kallsyms data contains all the non-stack symbols from a kernel + or a module. The kernel symbols are held between __start___kallsyms + and __stop___kallsyms. The symbols for a module are accessed via + the struct module chain which is based at module_list. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ident "$Id$" + +#ifndef MODUTILS_KALLSYMS_H +#define MODUTILS_KALLSYMS_H 1 + +/* Have to (re)define these ElfW entries here because external kallsyms + * code does not have access to modutils/include/obj.h. This code is + * included from user spaces tools (modutils) and kernel, they need + * different includes. + */ + +#ifndef ELFCLASS32 +#ifdef __KERNEL__ +#include +#else /* __KERNEL__ */ +#include +#endif /* __KERNEL__ */ +#endif /* ELFCLASS32 */ + +#ifndef ELFCLASSM +#define ELFCLASSM ELF_CLASS +#endif + +#ifndef ElfW +# if ELFCLASSM == ELFCLASS32 +# define ElfW(x) Elf32_ ## x +# define ELFW(x) ELF32_ ## x +# else +# define ElfW(x) Elf64_ ## x +# define ELFW(x) ELF64_ ## x +# endif +#endif + +/* Format of data in the kallsyms section. + * Most of the fields are small numbers but the total size and all + * offsets can be large so use the 32/64 bit types for these fields. + * + * Do not use sizeof() on these structures, modutils may be using extra + * fields. Instead use the size fields in the header to access the + * other bits of data. + */ + +struct kallsyms_header { + int size; /* Size of this header */ + ElfW(Word) total_size; /* Total size of kallsyms data */ + int sections; /* Number of section entries */ + ElfW(Off) section_off; /* Offset to first section entry */ + int section_size; /* Size of one section entry */ + int symbols; /* Number of symbol entries */ + ElfW(Off) symbol_off; /* Offset to first symbol entry */ + int symbol_size; /* Size of one symbol entry */ + ElfW(Off) string_off; /* Offset to first string */ + ElfW(Addr) start; /* Start address of first section */ + ElfW(Addr) end; /* End address of last section */ +}; + +struct kallsyms_section { + ElfW(Addr) start; /* Start address of section */ + ElfW(Word) size; /* Size of this section */ + ElfW(Off) name_off; /* Offset to section name */ + ElfW(Word) flags; /* Flags from section */ +}; + +struct kallsyms_symbol { + ElfW(Off) section_off; /* Offset to section that owns this symbol */ + ElfW(Addr) symbol_addr; /* Address of symbol */ + ElfW(Off) name_off; /* Offset to symbol name */ +}; + +#define KALLSYMS_SEC_NAME "__kallsyms" +#define KALLSYMS_IDX 2 /* obj_kallsyms creates kallsyms as section 2 */ + +#define kallsyms_next_sec(h,s) \ + ((s) = (struct kallsyms_section *)((char *)(s) + (h)->section_size)) +#define kallsyms_next_sym(h,s) \ + ((s) = (struct kallsyms_symbol *)((char *)(s) + (h)->symbol_size)) + +int kallsyms_symbol_to_address( + const char *name, /* Name to lookup */ + unsigned long *token, /* Which module to start with */ + const char **mod_name, /* Set to module name or "kernel" */ + unsigned long *mod_start, /* Set to start address of module */ + unsigned long *mod_end, /* Set to end address of module */ + const char **sec_name, /* Set to section name */ + unsigned long *sec_start, /* Set to start address of section */ + unsigned long *sec_end, /* Set to end address of section */ + const char **sym_name, /* Set to full symbol name */ + unsigned long *sym_start, /* Set to start address of symbol */ + unsigned long *sym_end /* Set to end address of symbol */ + ); + +int kallsyms_address_to_symbol( + unsigned long address, /* Address to lookup */ + const char **mod_name, /* Set to module name */ + unsigned long *mod_start, /* Set to start address of module */ + unsigned long *mod_end, /* Set to end address of module */ + const char **sec_name, /* Set to section name */ + unsigned long *sec_start, /* Set to start address of section */ + unsigned long *sec_end, /* Set to end address of section */ + const char **sym_name, /* Set to full symbol name */ + unsigned long *sym_start, /* Set to start address of symbol */ + unsigned long *sym_end /* Set to end address of symbol */ + ); + +#endif /* kallsyms.h */ diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/linux/kdb.h linux/include/linux/kdb.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/linux/kdb.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/kdb.h Tue Jun 13 16:30:11 2000 @@ -0,0 +1,186 @@ +/* + * Minimalist Kernel Debugger + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ + + +#if !defined(__KDB_H) +#define __KDB_H + +#include + +#define KDB_MAJOR_VERSION 1 +#define KDB_MINOR_VERSION 2 + + /* + * kdb_active is initialized to zero, and is set + * to KDB_REASON_xxx whenever the kernel debugger is entered. + */ +extern volatile int kdb_active; + + /* + * kdb_port is initialized to zero, and is set to the I/O port + * address of the serial port when the console is setup in + * serial_console_setup. + */ +extern int kdb_port; + + /* + * kdb_new_cpu is used in the cpu switching code to select the + * next processor to run. + */ +extern volatile int kdb_new_cpu; + + /* + * 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 kdba_getword. 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 + /* + * KDB_FLAG_SS is set when the kernel debugger is using the + * processor trap flag to single-step a processor. If a single + * step trap occurs and the processor was not executing in kernel + * mode when the SS trap occurred and this flag is clear, + * the SS trap will be ignored by KDB and the kernel will be + * allowed to deal with it as necessary (e.g. for ptrace). + */ +#define KDB_FLAG_SS 0x00000010 + /* KDB_FLAG_SSBPT is set when the kernel debugger must single step + * a task in order to re-establish an instruction breakpoint which + * uses the instruction replacement mechanism. + */ +#define KDB_FLAG_SSBPT 0x00000020 + + /* + * Internal debug flags + */ +#define KDB_DEBUG_BT 0x01000000 /* Stack traceback debug */ +#define KDB_DEBUG_BP 0x02000000 /* Breakpoint subsystem debug */ +#define KDB_DEBUG_LBR 0x04000000 /* Print last branch register */ +#define KDB_DEBUG_AR 0x08000000 /* Activation record, generic */ +#define KDB_DEBUG_ARA 0x10000000 /* Activation record, arch specific */ +#define KDB_DEBUG_MASK 0xff000000 /* All debug flags */ +#define KDB_DEBUG_SHIFT 24 /* Shift factor for dbflags */ + +extern volatile int kdb_flags; + + /* + * External entry point for the kernel debugger. The pt_regs + * at the time of entry are supplied along with the reason for + * entry to the kernel debugger. + */ + +typedef enum { + KDB_REASON_ENTER = 1, /* Call kdb() directly - regs invalid */ + KDB_REASON_FAULT, /* Kernel fault - regs valid */ + KDB_REASON_BREAK, /* Breakpoint inst. - regs valid */ + KDB_REASON_DEBUG, /* Debug Fault - regs valid */ + KDB_REASON_PANIC, /* Kernel Panic - regs valid */ + KDB_REASON_SWITCH, /* CPU switch - regs valid*/ + KDB_REASON_INT, /* KDB_ENTER trap - regs valid */ + KDB_REASON_KEYBOARD, /* Keyboard entry - regs valid */ + KDB_REASON_NMI /* Non-maskable interrupt; regs valid */ +} kdb_reason_t; + +extern int kdb(kdb_reason_t reason, int error_code, kdb_eframe_t); + +typedef int (*kdb_func_t)(int, const char **, const char **, kdb_eframe_t); + + /* + * Symbol table format returned by kallsyms. + */ + +typedef struct __ksymtab { + unsigned long value; /* Address of symbol */ + const char *mod_name; /* Module containing symbol or "kernel" */ + unsigned long mod_start; + unsigned long mod_end; + const char *sec_name; /* Section containing symbol */ + unsigned long sec_start; + unsigned long sec_end; + const char *sym_name; /* Full symbol name, including any version */ + unsigned long sym_start; + unsigned long sym_end; + } kdb_symtab_t; + + /* + * Exported Symbols for kernel loadable modules to use. + */ +extern int kdb_register(char *, kdb_func_t, char *, char *, short); +extern int kdb_unregister(char *); + +extern unsigned long kdba_getword(unsigned long, size_t); +extern unsigned long kdba_putword(unsigned long, size_t, unsigned long); + +extern int kdbgetularg(const char *, unsigned long *); +extern char *kdbgetenv(const char *); +extern int kdbgetintenv(const char *, int *); +extern int kdbgetaddrarg(int, const char**, int*, unsigned long *, + long *, char **, kdb_eframe_t); +extern int kdbgetsymval(const char *, kdb_symtab_t *); +extern int kdbnearsym(unsigned long, kdb_symtab_t *); +extern int kdb_printf(const char *,...) + __attribute__ ((format (printf, 1, 2))); +extern void kdb_init(void); +extern void kdb_symbol_print(kdb_machreg_t, const kdb_symtab_t *, unsigned int); + +#if defined(CONFIG_SMP) + /* + * Kernel debugger non-maskable IPI handler. + */ +extern int kdb_ipi(kdb_eframe_t, void (*ack_interrupt)(void)); +extern void smp_kdb_stop(void); +#endif + + /* + * Interface from general kernel to enable any hardware + * error reporting mechanisms. Such as the Intel Machine + * Check Architecture, for example. + */ +extern void kdb_enablehwfault(void); + + /* + * Interface from kernel trap handling code to kernel debugger. + */ +extern int kdba_callback_die(struct pt_regs *, int, long, void*); +extern int kdba_callback_bp(struct pt_regs *, int, long, void*); +extern int kdba_callback_debug(struct pt_regs *, int, long, void *); +#endif /* __KDB_H */ diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/linux/kdbprivate.h linux/include/linux/kdbprivate.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/linux/kdbprivate.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/kdbprivate.h Tue Jun 13 16:30:11 2000 @@ -0,0 +1,297 @@ +/* + * Minimalist Kernel Debugger + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ +#if !defined(_KDBPRIVATE_H) +#define _KDBPRIVATE_H + +#include +#include + +#include + +/* + * Kernel Debugger Error codes + */ + +#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 + + /* + * kdb_nextline + * + * Contains the current line number on the screen. Used + * to handle the built-in pager (LINES env variable) + */ +extern int kdb_nextline; + + /* + * kdb_diemsg + * + * Contains a pointer to the last string supplied to the + * kernel 'die' panic function. + */ +extern char *kdb_diemsg; + + /* + * Breakpoint state + * + * Each active and inactive breakpoint is represented by + * an instance of the following data structure. + */ + +typedef struct _kdb_bp { + bfd_vma bp_addr; /* Address breakpoint is present at */ + kdb_machinst_t bp_inst; /* Replaced instruction */ + + unsigned int bp_free:1; /* This entry is available */ + + unsigned int bp_enabled:1; /* Breakpoint is active in register */ + unsigned int bp_global:1; /* Global to all processors */ + + unsigned int bp_hardtype:1; /* Uses hardware register */ + unsigned int bp_forcehw:1; /* Force hardware register */ + unsigned int bp_instvalid:1; /* 0=bp_inst invalid, 1=bp_inst valid */ + unsigned int bp_installed:1; /* Breakpoint is installed */ + unsigned int bp_delay:1; /* Do delayed bp handling */ + unsigned int bp_delayed:1; /* Delayed breakpoint */ + + int bp_cpu; /* Cpu # (if bp_global == 0) */ + kdbhard_bp_t bp_template; /* Hardware breakpoint template */ + kdbhard_bp_t *bp_hard; /* Hardware breakpoint structure */ + int bp_adjust; /* Adjustment to PC for real instruction */ +} kdb_bp_t; + + /* + * Breakpoint handling subsystem global variables + */ +extern kdb_bp_t kdb_breakpoints[/* KDB_MAXBPT */]; + + /* + * Breakpoint architecture dependent functions. Must be provided + * in some form for all architectures. + */ +extern void kdba_initbp(void); +extern void kdba_printbp(kdb_bp_t *); +extern void kdba_printbpreg(kdbhard_bp_t *); +extern kdbhard_bp_t *kdba_allocbp(kdbhard_bp_t *, int *); +extern void kdba_freebp(kdbhard_bp_t *); +extern int kdba_parsebp(int, const char**, int *, kdb_bp_t*); +extern char *kdba_bptype(kdbhard_bp_t *); +extern void kdba_setsinglestep(kdb_eframe_t); + + /* + * KDB-only global function prototypes. + */ +extern void kdb_id1(unsigned long); +extern void kdb_id_init(void); + + /* + * Architecture dependent function to enable any + * processor machine check exception handling modes. + */ +extern void kdba_enable_mce(void); + +extern void kdba_enable_lbr(void); +extern void kdba_disable_lbr(void); +extern void kdba_print_lbr(void); + +extern int kdba_ipi(kdb_eframe_t, void (*)(void)); + + /* + * Initialization functions. + */ +extern void kdba_init(void); +extern void kdb_io_init(void); + + /* + * Architecture specific function to read a string. + */ +extern char * kdba_read(char *, size_t); + + /* + * Data for a single activation record on stack. + */ + +typedef struct __kdb_activation_record { + kdb_machreg_t start; /* -> start of activation record */ + kdb_machreg_t end; /* -> end+1 of activation record */ + kdb_machreg_t ret; /* Return address to caller */ + kdb_machreg_t oldfp; /* Frame pointer for caller's frame */ + kdb_machreg_t fp; /* Frame pointer for callee's frame */ + kdb_machreg_t arg0; /* -> First argument on stack (in previous ar) */ + int locals; /* Bytes allocated for local variables */ + int regs; /* Bytes allocated for saved registers */ + int args; /* Bytes allocated for arguments (in previous ar) */ + int setup; /* Bytes allocated for setup data */ +} kdb_ar_t; + + /* + * General Stack Traceback functions. + */ + +extern int kdb_get_next_ar(kdb_machreg_t, kdb_machreg_t, + kdb_machreg_t, kdb_machreg_t, + kdb_machreg_t, + kdb_ar_t *, kdb_symtab_t *); + + /* + * Architecture specific Stack Traceback functions. + */ + +struct task_struct; + +extern int kdba_bt_stack(struct pt_regs *, kdb_machreg_t *, + int, struct task_struct *); +extern int kdba_bt_process(struct task_struct *, int); +extern int kdba_prologue(const kdb_symtab_t *, kdb_machreg_t, + kdb_machreg_t, kdb_machreg_t, kdb_machreg_t, + int, kdb_ar_t *); + /* + * KDB Command Table + */ + +typedef struct _kdbtab { + char *cmd_name; /* Command name */ + kdb_func_t cmd_func; /* Function to execute command */ + char *cmd_usage; /* Usage String for this command */ + char *cmd_help; /* Help message for this command */ + short cmd_flags; /* Parsing flags */ + short cmd_minlen; /* Minimum legal # command chars required */ +} kdbtab_t; + + /* + * External command function declarations + */ + +extern int kdb_id(int, const char **, const char **, kdb_eframe_t); +extern int kdb_bp(int, const char **, const char **, kdb_eframe_t); +extern int kdb_bc(int, const char **, const char **, kdb_eframe_t); +extern int kdb_bt(int, const char **, const char **, kdb_eframe_t); +extern int kdb_ss(int, const char **, const char **, kdb_eframe_t); + + /* + * External utility function declarations + */ +extern char* kdb_getstr(char *, size_t, char *); + + /* + * Register contents manipulation + */ +extern int kdba_getregcontents(const char *, kdb_eframe_t, kdb_machreg_t *); +extern int kdba_setregcontents(const char *, kdb_eframe_t, kdb_machreg_t); +extern int kdba_dumpregs(struct pt_regs *, const char *, const char *); +extern int kdba_setpc(kdb_eframe_t, kdb_machreg_t); +extern kdb_machreg_t kdba_getpc(kdb_eframe_t); + + /* + * Debug register handling. + */ +extern void kdba_installdbreg(kdb_bp_t*); +extern void kdba_removedbreg(kdb_bp_t*); + + /* + * Breakpoint handling - External interfaces + */ +extern void kdb_initbptab(void); +extern void kdb_bp_install_global(kdb_eframe_t); +extern void kdb_bp_install_local(kdb_eframe_t); +extern void kdb_bp_remove_global(void); +extern void kdb_bp_remove_local(void); + + /* + * Breakpoint handling - Internal to kdb_bp.c/kdba_bp.c + */ +extern void kdba_installbp(kdb_eframe_t ef, kdb_bp_t *); +extern void kdba_removebp(kdb_bp_t *); + + +typedef enum { + KDB_DB_BPT, /* Breakpoint */ + KDB_DB_SS, /* Single-step trap */ + KDB_DB_SSB, /* Single step to branch */ + KDB_DB_NOBPT /* Spurious breakpoint */ +} kdb_dbtrap_t; + +extern kdb_dbtrap_t kdba_db_trap(kdb_eframe_t); /* DEBUG trap/fault handler */ +extern kdb_dbtrap_t kdba_bp_trap(kdb_eframe_t); /* Breakpoint trap/fault hdlr */ + + /* + * Interrupt Handling + */ +typedef int kdb_intstate_t; + +extern void kdba_disableint(kdb_intstate_t *); +extern void kdba_restoreint(kdb_intstate_t *); + + /* + * General Disassembler interfaces + */ +extern int kdb_dis_fprintf_dummy(FILE*, const char *, ...); +extern disassemble_info kdb_di; + + /* + * Architecture Dependent Disassembler interfaces + */ +extern void kdba_printaddress(kdb_machreg_t, disassemble_info *, int); +extern int kdba_id_printinsn(kdb_machreg_t, disassemble_info *); +extern int kdba_id_parsemode(const char *, disassemble_info*); +extern void kdba_id_init(disassemble_info *); + + /* + * Miscellaneous functions + */ +extern int kdb_getcurrentframe(kdb_eframe_t); +extern void kdb_resetkeyboard(void); + + /* + * Defines for kdb_symbol_print. + */ +#define KDB_SP_SPACEB 0x0001 /* Space before string */ +#define KDB_SP_SPACEA 0x0002 /* Space after string */ +#define KDB_SP_PAREN 0x0004 /* Parenthesis around string */ +#define KDB_SP_VALUE 0x0008 /* Print the value of the address */ +#define KDB_SP_SYMSIZE 0x0010 /* Print the size of the symbol */ +#define KDB_SP_NEWLINE 0x0020 /* Newline after string */ +#define KDB_SP_DEFAULT (KDB_SP_VALUE|KDB_SP_PAREN) + +#endif /* !_KDBPRIVATE_H */ + diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/linux/module.h linux/include/linux/module.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/linux/module.h Tue Jun 20 03:24:47 2000 +++ linux/include/linux/module.h Mon Jun 19 02:27:44 2000 @@ -78,11 +78,14 @@ unsigned long gp; #endif /* Members past this point are extensions to the basic - module support and are optional. Use mod_opt_member() + module support and are optional. Use mod_member_present() to examine them. */ const struct module_persist *persist_start; const struct module_persist *persist_end; int (*can_unload)(void); + int runsize; /* In modutils, not currently used */ + const char *kallsyms_start; + const char *kallsyms_end; }; struct module_info diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/include/linux/pktcdvd.h linux/include/linux/pktcdvd.h --- /opt/kernel/linux-2.4.0-test1-ac21/include/linux/pktcdvd.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/pktcdvd.h Mon Jun 19 23:10:29 2000 @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2000 Jens Axboe + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and + * DVD-RW devices. + * + */ + +#ifndef PKTCDVD_H +#define PKTCDVD_H + +#define PACKET_MAJOR 97 + +/* + * device types + */ +#define PACKET_CDR 1 +#define PACKET_CDRW 2 +#define PACKET_DVDR 3 +#define PACKET_DVDRW 4 + +/* + * flags + */ +#define PACKET_WRITEABLE 1 +#define PACKET_NWA_VALID 2 +#define PACKET_LRA_VALID 3 +#define PACKET_READY 4 +#define PACKET_READONLY 5 + +/* + * Disc status -- from READ_DISC_INFO + */ +#define PACKET_DISC_EMPTY 0 +#define PACKET_DISC_INCOMPLETE 1 +#define PACKET_DISC_COMPLETE 2 +#define PACKET_DISC_OTHER 3 + +/* + * Last session/border status + */ +#define PACKET_SESSION_EMPTY 0 +#define PACKET_SESSION_INCOMPLETE 1 +#define PACKET_SESSION_RESERVED 2 +#define PACKET_SESSION_COMPLETE 3 + +#define PACKET_MCN "4a656e734178626f65323030300000" + +#undef PACKET_USE_LS + +/* + * Very crude stats for now + */ +struct packet_stats { + unsigned long bh_s; + unsigned long bh_e; + unsigned long blocks_written; + unsigned long blocks_read; +}; + +/* + * packet ioctls + */ +#define PACKET_IOCTL_MAGIC ('X') +#define PACKET_GET_STATS _IOR(PACKET_IOCTL_MAGIC, 0, struct packet_stats) +#define PACKET_SETUP_DEV _IOW(PACKET_IOCTL_MAGIC, 1, unsigned int) +#define PACKET_TEARDOWN_DEV _IOW(PACKET_IOCTL_MAGIC, 2, unsigned int) + +#ifdef __KERNEL__ +#include + +struct packet_settings { + __u8 size; /* packet size in frames */ + __u8 fp; /* fixed packets */ + __u8 link_loss; /* the rest is specified + * as per Mt Fuji */ + __u8 write_type; + __u8 track_mode; + __u8 block_mode; +}; + +struct pktcdvd_device { + kdev_t dev; /* dev attached */ + kdev_t pkt_dev; /* our dev */ + char name[20]; + struct cdrom_device_info *cdi; /* cdrom matching dev */ + struct packet_settings settings; + struct packet_stats stats; + atomic_t refcnt; + __u8 speed; /* cur write speed */ + __u32 offset; /* start offset */ + __u8 mode_offset; /* 0 / 8 */ + __u8 type; + __u32 flags; + __u8 disc_status; + __u8 track_status; /* last one */ + __u32 nwa; /* next writable address */ + __u32 lra; /* last recorded address */ + spinlock_t lock; + struct list_head pkt_queue; + struct semaphore pkt_sem; + merge_request_fn *front_merge_fn; + merge_request_fn *back_merge_fn; + merge_request_fn *hole_merge_fn; + merge_requests_fn *merge_requests_fn; + request_queue_t queue; + struct dentry *pkt_dentry; +}; + +/* + * following possibly belongs in cdrom.h + */ + +struct cdvd_capacity { + __u32 lba; + __u32 block_length; +}; + +void pkt_elevator_dequeue(struct request *rq) {} + +#define ELEVATOR_PKTCDVD \ +((elevator_t) { \ + 0, /* sequence */ \ + \ + 0, /* read_latency */ \ + 0, /* write_latency */ \ + 0, /* max_bomb_segments */ \ + \ + 0, /* nr_segments */ \ + 0, /* read_pendings */ \ + \ + "pktcdvd_elv", /* elevator_name */ \ + \ + pkt_elevator, /* elevator_fn */ \ + pkt_elevator_merge, /* elevator_merge_fn */ \ + pkt_elevator_dequeue, /* dequeue_fn */ \ + }) + +#endif /* __KERNEL__ */ + +#endif /* PKTCDVD_H */ diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/init/main.c linux/init/main.c --- /opt/kernel/linux-2.4.0-test1-ac21/init/main.c Tue Jun 20 03:24:47 2000 +++ linux/init/main.c Sat Jun 17 23:43:54 2000 @@ -67,6 +67,10 @@ #include #endif +#if defined(CONFIG_KDB) +#include +#endif + /* * Versions of gcc older than that listed below may actually compile * and link okay, but the end product can have subtle run time bugs. @@ -415,6 +419,12 @@ } if (next != NULL) *next++ = 0; +#if defined(CONFIG_KDB) + if (!strcmp(line, "kdb")) { + kdb_flags |= KDB_FLAG_EARLYKDB; + continue; + } +#endif if (!strncmp(line,"init=",5)) { line += 5; execute_command = line; @@ -522,6 +532,12 @@ kmem_cache_init(); sti(); calibrate_delay(); +#if defined(CONFIG_KDB) + kdb_init(); + if (kdb_flags & KDB_FLAG_EARLYKDB) { + KDB_ENTER(); + } +#endif #ifdef CONFIG_BLK_DEV_INITRD if (initrd_start && !initrd_below_start_ok && initrd_start < min_low_pfn << PAGE_SHIFT) { @@ -531,6 +547,7 @@ } #endif mem_init(); + kmem_cache_sizes_init(); #ifdef CONFIG_3215_CONSOLE con3215_activate(); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/kdb/Makefile linux/kdb/Makefile --- /opt/kernel/linux-2.4.0-test1-ac21/kdb/Makefile Thu Jan 1 01:00:00 1970 +++ linux/kdb/Makefile Tue Jun 13 16:30:11 2000 @@ -0,0 +1,8 @@ +O_TARGET := kdb.o +O_OBJS = kdbmain.o kdb_bt.o kdb_bp.o kdb_id.o kdb_io.o kdbsupport.o + +MOD_SUB_DIRS += modules + +override CFLAGS := $(CFLAGS:%-pg=% ) + +include $(TOPDIR)/Rules.make diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/kdb/kdb_bp.c linux/kdb/kdb_bp.c --- /opt/kernel/linux-2.4.0-test1-ac21/kdb/kdb_bp.c Thu Jan 1 01:00:00 1970 +++ linux/kdb/kdb_bp.c Tue Jun 13 16:30:11 2000 @@ -0,0 +1,609 @@ +/* + * Kernel Debugger Breakpoint Handler + * + * Copyright 1999, Silicon Graphics, Inc. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Keith Owens 2000/05/23 + * KDB v1.2 + */ + +#include +#include +#include +#include +#if defined(CONFIG_SMP) +#include +#include +#endif +#include + +/* + * Table of kdb_breakpoints + */ +kdb_bp_t kdb_breakpoints[KDB_MAXBPT]; + +/* + * kdb_bp_install_global + * + * Install global kdb_breakpoints prior to returning from the + * kernel debugger. This allows the kdb_breakpoints to be set + * upon functions that are used internally by kdb, such as + * printk(). + * + * Parameters: + * ef Execution frame. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + * This function is only called once per kdb session. + */ + +void +kdb_bp_install_global(kdb_eframe_t ef) +{ + int i; + + for(i=0; ibp_forcehw) { + kdb_printf("Forced "); + } + + if (!bp->bp_template.bph_free) { + kdb_printf("%s ", kdba_bptype(&bp->bp_template)); + } else { + kdb_printf("Instruction(i) "); + } + + kdb_printf("BP #%d at ", i); + kdb_symbol_print(bp->bp_addr, NULL, KDB_SP_DEFAULT); + + if (bp->bp_enabled) { + kdba_printbp(bp); + if (bp->bp_global) + kdb_printf(" globally"); + else + kdb_printf(" on cpu %d", bp->bp_cpu); + if (bp->bp_adjust) + kdb_printf(" adjust %d", bp->bp_adjust); + } else { + kdb_printf("\n is disabled"); + } + + kdb_printf("\n"); +} + +/* + * kdb_bp + * + * Handle the bp, and bpa commands. + * + * [bp|bpa|bph] [DATAR|DATAW|IO [length]] + * + * Parameters: + * argc Count of arguments in argv + * argv Space delimited command line arguments + * envp Environment value + * regs Exception frame at entry to kernel debugger + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic if failure. + * Locking: + * None. + * Remarks: + * + * bp Set breakpoint. Only use hardware assist if necessary. + * bpa Set breakpoint on all cpus, only use hardware regs if necessary + * bph Set breakpoint - force hardware register + * bpha Set breakpoint on all cpus, force hardware register + */ + +int +kdb_bp(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + kdb_bp_t *bp; + int diag; + int free, same; + kdb_machreg_t addr; + char *symname = NULL; + long offset = 0ul; + int nextarg; + int hardware; + int global; + + if (argc == 0) { + /* + * Display breakpoint table + */ + for(i=0,bp=kdb_breakpoints; ibp_free) continue; + + kdb_printbp(bp, i); + } + + return 0; + } + + global = ((strcmp(argv[0], "bpa") == 0) + || (strcmp(argv[0], "bpha") == 0)); + hardware = ((strcmp(argv[0], "bph") == 0) + || (strcmp(argv[0], "bpha") == 0)); + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, &symname, regs); + if (diag) + return diag; + + /* + * Allocate a new bp structure + */ + free = same = KDB_MAXBPT; + for(i=0,bp=kdb_breakpoints; ibp_free) { + break; + } + } + + if (i == KDB_MAXBPT) + return KDB_TOOMANYBPT; + + bp->bp_addr = addr; + bp->bp_adjust = 0; + + bp->bp_forcehw = hardware; + if (kdb_flags & KDB_DEBUG_BP) + kdb_printf("kdb_bp: forcehw is %d hardware is %d\n", bp->bp_forcehw, hardware); + + /* + * Handle architecture dependent parsing + */ + diag = kdba_parsebp(argc, argv, &nextarg, bp); + if (diag) { + return diag; + } + + bp->bp_enabled = 1; + bp->bp_free = 0; + bp->bp_global = 1; /* Most breakpoints are global */ + + if (hardware && !global) { + bp->bp_global = 0; +#if defined(CONFIG_SMP) + bp->bp_cpu = smp_processor_id(); +#endif + } + + /* + * Allocate a hardware breakpoint. If one is not available, + * disable the breakpoint, but leave it in the breakpoint + * table. When the breakpoint is re-enabled (via 'be'), we'll + * attempt to allocate a hardware register for it. + */ + if (!bp->bp_template.bph_free) { + bp->bp_hard = kdba_allocbp(&bp->bp_template, &diag); + if (diag) { + bp->bp_enabled = 0; + return diag; + } + bp->bp_hardtype = 1; + } + + kdb_printbp(bp, i); + + return 0; +} + +/* + * kdb_bc + * + * Handles the 'bc', 'be', and 'bd' commands + * + * [bd|bc|be] + * + * Parameters: + * argc Count of arguments in argv + * argv Space delimited command line arguments + * envp Environment value + * regs Exception frame at entry to kernel debugger + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + */ +#define KDBCMD_BC 0 +#define KDBCMD_BE 1 +#define KDBCMD_BD 2 + +int +kdb_bc(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + kdb_machreg_t addr; + kdb_bp_t *bp = 0; + int lowbp = KDB_MAXBPT; + int highbp = 0; + int done = 0; + int i; + int diag; + int cmd; /* KDBCMD_B? */ + + if (strcmp(argv[0], "be") == 0) { + cmd = KDBCMD_BE; + } else if (strcmp(argv[0], "bd") == 0) { + cmd = KDBCMD_BD; + } else + cmd = KDBCMD_BC; + + if (argc != 1) + return KDB_ARGCOUNT; + + if (strcmp(argv[1], "*") == 0) { + lowbp = 0; + highbp = KDB_MAXBPT; + } else { + diag = kdbgetularg(argv[1], &addr); + if (diag) + return diag; + + /* + * For addresses less than the maximum breakpoint number, + * assume that the breakpoint number is desired. + */ + if (addr < KDB_MAXBPT) { + bp = &kdb_breakpoints[addr]; + lowbp = highbp = addr; + highbp++; + } else { + for(i=0, bp=kdb_breakpoints; ibp_addr == addr) { + lowbp = highbp = i; + highbp++; + break; + } + } + } + } + + /* + * Now operate on the set of breakpoints matching the input + * criteria (either '*' for all, or an individual breakpoint). + */ + for(bp=&kdb_breakpoints[lowbp], i=lowbp; + i < highbp; + i++, bp++) { + if (bp->bp_free) + continue; + + done++; + + switch (cmd) { + case KDBCMD_BC: + if (bp->bp_hardtype) { + kdba_freebp(bp->bp_hard); + bp->bp_hard = 0; + bp->bp_hardtype = 0; + } + + bp->bp_enabled = 0; + bp->bp_global = 0; + + kdb_printf("Breakpoint %d at 0x%lx cleared\n", + i, bp->bp_addr); + + bp->bp_addr = 0; + bp->bp_free = 1; + + break; + case KDBCMD_BE: + /* + * Allocate a hardware breakpoint. If one is not + * available, don't enable the breakpoint. + */ + if (!bp->bp_template.bph_free + && !bp->bp_hardtype) { + bp->bp_hard = kdba_allocbp(&bp->bp_template, &diag); + if (diag) { + bp->bp_enabled = 0; + return diag; + } + bp->bp_hardtype = 1; + } + + bp->bp_enabled = 1; + + kdb_printf("Breakpoint %d at 0x%lx in enabled", + i, bp->bp_addr); + + kdb_printf("\n"); + break; + case KDBCMD_BD: + if (!bp->bp_enabled) { + return 0; + } + + /* + * Since this breakpoint is now disabled, we can + * give up the hardware register which is allocated + * to it. + */ + if (bp->bp_hardtype) { + kdba_freebp(bp->bp_hard); + bp->bp_hard = 0; + bp->bp_hardtype = 0; + } + + bp->bp_enabled = 0; + + kdb_printf("Breakpoint %d at 0x%lx disabled\n", + i, bp->bp_addr); + + break; + } + } + + return (!done)?KDB_BPTNOTFOUND:0; +} +/* + * kdb_ss + * + * Process the 'ss' (Single Step) and 'ssb' (Single Step to Branch) + * commands. + * + * ss [] + * ssb + * + * Parameters: + * argc Argument count + * argv Argument vector + * envp Environment vector + * regs Registers at time of entry to kernel debugger + * Outputs: + * None. + * Returns: + * 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, kdb_eframe_t ef) +{ + int ssb = 0; + + ssb = (strcmp(argv[0], "ssb") == 0); + if ((ssb && (argc != 0)) + || (!ssb && (argc > 1))) { + return KDB_ARGCOUNT; + } + +#if 0 + /* + * Fetch provided count + */ + diag = kdbgetularg(argv[1], &sscount); + if (diag) + return diag; +#endif + + /* + * Print current insn + */ + kdb_id1(kdba_getpc(ef)); + + /* + * Set trace flag and go. + */ + kdb_flags |= KDB_FLAG_SS; + if (ssb) + kdb_flags |= KDB_FLAG_SSB; + + kdba_setsinglestep(ef); /* Enable single step */ + + return KDB_GO; +} + +/* + * kdb_initbptab + * + * Initialize the breakpoint table. Register breakpoint commands. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdb_initbptab(void) +{ + int i; + kdb_bp_t *bp; + + /* + * First time initialization. + */ + memset(&kdb_breakpoints, '\0', sizeof(kdb_breakpoints)); + + for (i=0, bp=kdb_breakpoints; ibp_free = 1; + /* + * The bph_free flag is architecturally required. It + * is set by architecture-dependent code to false (zero) + * in the event a hardware breakpoint register is required + * for this breakpoint. + * + * The rest of the template is reserved to the architecture + * dependent code and _must_ not be touched by the architecture + * independent code. + */ + bp->bp_template.bph_free = 1; + } + + kdb_register("bp", kdb_bp, "[]", "Set/Display breakpoints", 0); + kdb_register("bl", kdb_bp, "[]", "Display breakpoints", 0); + kdb_register("bpa", kdb_bp, "[]", "Set/Display global breakpoints", 0); + kdb_register("bph", kdb_bp, "[]", "Set hardware breakpoint", 0); + kdb_register("bpha", kdb_bp, "[]", "Set global hardware breakpoint", 0); + kdb_register("bc", kdb_bc, "", "Clear Breakpoint", 0); + kdb_register("be", kdb_bc, "", "Enable Breakpoint", 0); + kdb_register("bd", kdb_bc, "", "Disable Breakpoint", 0); + + kdb_register("ss", kdb_ss, "[<#steps>]", "Single Step", 1); + kdb_register("ssb", kdb_ss, "", "Single step to branch/call", 0); + /* + * Architecture dependent initialization. + */ + kdba_initbp(); +} + diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/kdb/kdb_bt.c linux/kdb/kdb_bt.c --- /opt/kernel/linux-2.4.0-test1-ac21/kdb/kdb_bt.c Thu Jan 1 01:00:00 1970 +++ linux/kdb/kdb_bt.c Tue Jun 13 16:30:11 2000 @@ -0,0 +1,131 @@ +/* + * Minimalist Kernel Debugger - Architecture independent stack traceback + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + * Masahiro Adegawa 1999/12/01 + * 'sr' command, active flag in 'ps' + * Scott Lurndal 1999/12/12 + * Significantly restructure for linux2.3 + * Keith Owens 2000/05/23 + * KDB v1.2 + * + */ + +#include +#include +#include +#include +#include +#include +#include + + +/* + * kdb_bt + * + * This function implements the 'bt' command. Print a stack + * traceback. + * + * bt [] (addr-exp is for alternate stacks) + * btp (Kernel stack for ) + * + * address expression refers to a return address on the stack. It + * is expected to be preceeded by a frame pointer. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * ef registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Backtrack works best when the code uses frame pointers. But + * even without frame pointers we should get a reasonable trace. + * + * mds comes in handy when examining the stack to do a manual + * traceback. + */ + +int +kdb_bt(int argc, const char **argv, const char **envp, kdb_eframe_t ef) +{ + int diag; + int argcount = 5; + char buffer[80]; + int nextarg; + unsigned long addr; + long offset; + + /* + * Determine how many possible arguments to print. + */ + diag = kdbgetintenv("BTARGS", &argcount); + + if (strcmp(argv[0], "bta") == 0) { + struct task_struct *p; + + for_each_task(p) { + kdb_printf("Stack traceback for pid %d\n", p->pid); + + diag = kdba_bt_process(p, argcount); + + kdb_getstr(buffer, sizeof(buffer), + "Enter to end, to continue:"); + + if (buffer[0] == 'q') { + return 0; + } + } + } else if (strcmp(argv[0], "btp") == 0) { + struct task_struct *p; + int pid; + + diag = kdbgetularg((char *)argv[1], (unsigned long*)&pid); + if (diag) + return diag; + + for_each_task(p) { + if (p->pid == pid) { + return kdba_bt_process(p, argcount); + } + } + + kdb_printf("No process with pid == %d found\n", pid); + return 0; + } else { + if (argc) { + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, + &offset, NULL, ef); + if (diag) + return diag; + + return kdba_bt_stack(ef, &addr, argcount, current); + } else { + return kdba_bt_stack(ef, NULL, argcount, current); + } + } + + /* NOTREACHED */ + return 0; +} diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/kdb/kdb_id.c linux/kdb/kdb_id.c --- /opt/kernel/linux-2.4.0-test1-ac21/kdb/kdb_id.c Thu Jan 1 01:00:00 1970 +++ linux/kdb/kdb_id.c Tue Jun 13 16:30:11 2000 @@ -0,0 +1,192 @@ +/* + * Minimalist Kernel Debugger - Architecture Independent Instruction Disassembly + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + * Masahiro Adegawa 1999/12/01 + * 'sr' command, active flag in 'ps' + * Scott Lurndal 1999/12/12 + * Significantly restructure for linux2.3 + * Keith Owens 2000/05/23 + * KDB v1.2 + * + */ + +#include +#include +#include +#include +#include +#include + +disassemble_info kdb_di; + +/* + * kdb_id + * + * Handle the id (instruction display) command. + * + * id [] + * + * Parameters: + * argc Count of arguments in argv + * argv Space delimited command line arguments + * envp Environment value + * regs Exception frame at entry to kernel debugger + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic if failure. + * Locking: + * None. + * Remarks: + */ + +int +kdb_id(int argc, const char **argv, const char **envp, struct pt_regs* regs) +{ + kdb_machreg_t pc; + int icount; + int diag; + int i; + char * mode; + int nextarg; + long offset = 0; + static kdb_machreg_t lastpc=0; + struct disassemble_info *dip = &kdb_di; + + if (argc != 1) { + if (lastpc == 0) { + return KDB_ARGCOUNT; + } else { + char lastbuf[50]; + sprintf(lastbuf, "0x%lx", lastpc); + argv[1] = lastbuf; + argc = 1; + } + } + + + /* + * Fetch PC. First, check to see if it is a symbol, if not, + * try address. + */ + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &pc, &offset, NULL, regs); + if (diag) + return diag; + + /* + * Number of lines to display + */ + diag = kdbgetintenv("IDCOUNT", &icount); + if (diag) + return diag; + + mode = kdbgetenv("IDMODE"); + diag = kdba_id_parsemode(mode, dip); + if (diag) { + return diag; + } + + for(i=0; i +#include +#include +#include +#include +#include +#include + +#include +#include + +static struct console *kdbcons = NULL; + +/* + * kdb_read + * + * This function reads a string of characters, terminated by + * a newline, or by reaching the end of the supplied buffer, + * from the current kernel debugger console device. + * Parameters: + * buffer - Address of character buffer to receive input characters. + * bufsize - size, in bytes, of the character buffer + * Returns: + * Returns a pointer to the buffer containing the received + * character string. This string will be terminated by a + * newline character. + * Locking: + * No locks are required to be held upon entry to this + * function. It is not reentrant - it relies on the fact + * that while kdb is running on any one processor all other + * processors will be spinning at the kdb barrier. + * Remarks: + * + * Davidm asks, why doesn't kdb use the console abstraction; + * here are some reasons: + * - you cannot debug the console abstraction with kdb if + * kdb uses it. + * - you rely on the correct functioning of the abstraction + * in the presence of general system failures. + * - You must acquire the console spinlock thus restricting + * the usability - what if the kernel fails with the spinlock + * held - one still wishes to debug such situations. + * - How about debugging before the console(s) are registered? + * - None of the current consoles (sercons, vt_console_driver) + * have read functions defined. + * - The standard pc keyboard and terminal drivers are interrupt + * driven. We cannot enable interrupts while kdb is active, + * so the standard input functions cannot be used by kdb. + * + * An implementation could be improved by removing the need for + * lock acquisition - just keep a 'struct console *kdbconsole;' global + * variable which refers to the preferred kdb console. + * + * The bulk of this function is architecture dependent. + */ + +char * +kdb_read(char *buffer, size_t bufsize) +{ + return kdba_read(buffer, bufsize); +} + +/* + * kdb_getstr + * + * Print the prompt string and read a command from the + * input device. + * + * Parameters: + * buffer Address of buffer to receive command + * bufsize Size of buffer in bytes + * prompt Pointer to string to use as prompt string + * Returns: + * Pointer to command buffer. + * Locking: + * None. + * Remarks: + * For SMP kernels, the processor number will be + * substituted for %d, %x or %o in the prompt. + */ + +char * +kdb_getstr(char *buffer, size_t bufsize, char *prompt) +{ +#if defined(CONFIG_SMP) + kdb_printf(prompt, smp_processor_id()); +#else + kdb_printf("%s", prompt); +#endif + return kdb_read(buffer, bufsize); +} + +/* + * kdb_printf + * + * Print a string to the output device(s). + * + * Parameters: + * printf-like format and optional args. + * Returns: + * 0 + * Locking: + * None. + * Remarks: + * use 'kdbcons->write()' to avoid polluting 'log_buf' with + * kdb output. + */ + +int +kdb_printf(const char *fmt, ...) +{ + char buffer[256]; + va_list ap; + int diag; + int linecount; + int logging, saved_loglevel = 0; + struct console *c = console_drivers; + + diag = kdbgetintenv("LINES", &linecount); + if (diag) + linecount = 22; + + diag = kdbgetintenv("LOGGING", &logging); + if (diag) + logging = 0; + + va_start(ap, fmt); + vsprintf(buffer, fmt, ap); + va_end(ap); + + /* + * Write to all consoles. + */ + while (c) { + c->write(c, buffer, strlen(buffer)); + c = c->next; + } + if (logging) { + spin_lock_irq(&console_lock); + saved_loglevel = console_loglevel; + console_loglevel = 0; + spin_unlock_irq(&console_lock); + printk("%s", buffer); + } + + if (strchr(buffer, '\n') != NULL) { + kdb_nextline++; + } + + if (kdb_nextline == linecount) { + char buf1[16]; +#if defined(CONFIG_SMP) + char buf2[32]; +#endif + 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 + + c = console_drivers; + while (c) { + c->write(c, moreprompt, strlen(moreprompt)); + c = c->next; + } + if (logging) + printk("%s", moreprompt); + + kdb_read(buf1, sizeof(buf1)); + + kdb_nextline = 1; + + if ((buf1[0] == 'q') + || (buf1[0] == 'Q')) { + if (logging) { + spin_lock_irq(&console_lock); + console_loglevel = saved_loglevel; + spin_unlock_irq(&console_lock); + } + kdb_longjmp(&kdbjmpbuf[smp_processor_id()], 1); + } + } + + if (logging) { + spin_lock_irq(&console_lock); + console_loglevel = saved_loglevel; + spin_unlock_irq(&console_lock); + } + return 0; +} + +/* + * kdb_io_init + * + * Initialize kernel debugger output environment. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * Select a console device. + */ + +void +kdb_io_init(void) +{ + /* + * Select a console. + */ + struct console *c = console_drivers; + + while (c) { + if ((c->flags & CON_CONSDEV)) { + kdbcons = c; + break; + } + c = c->next; + } + + if (kdbcons == NULL) { + long long i; + + printk("kdb: Initialization failed - no console\n"); + while (1) i++; + } + return; +} diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/kdb/kdbmain.c linux/kdb/kdbmain.c --- /opt/kernel/linux-2.4.0-test1-ac21/kdb/kdbmain.c Thu Jan 1 01:00:00 1970 +++ linux/kdb/kdbmain.c Tue Jun 13 16:30:11 2000 @@ -0,0 +1,2030 @@ +/* + * Minimalist Kernel Debugger + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + * Masahiro Adegawa 1999/12/01 + * 'sr' command, active flag in 'ps' + * Scott Lurndal 1999/12/12 + * Significantly restructure for linux2.3 + * Keith Owens 2000/05/23 + * KDB v1.2 + * + */ + +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_SMP) +#include +#endif +#include +#include + +#include + + /* + * Kernel debugger state flags + */ +volatile int kdb_flags = 0; + + /* + * kdb_lock protects updates to kdb_active. Used to + * single thread processors through the kernel debugger. + */ +spinlock_t kdb_lock = SPIN_LOCK_UNLOCKED; +volatile int kdb_active = 0; + +int kdb_nextline = 1; +#if defined(CONFIG_SMP) +int kdb_new_cpu = -1; +#endif + + /* + * Must have a setjmp buffer per CPU. Switching cpus will + * cause the jump buffer to be setup for the new cpu, and + * subsequent switches (and pager aborts) will use the + * appropriate per-processor values. + */ +kdb_jmp_buf kdbjmpbuf[NR_CPUS]; + + /* + * kdb_commands describes the available commands. + */ +static kdbtab_t kdb_commands[KDB_MAX_COMMANDS]; + +typedef struct _kdbmsg { + int km_diag; /* kdb diagnostic */ + char *km_msg; /* Corresponding message text */ +} kdbmsg_t; + +#define KDBMSG(msgnum, text) \ + { KDB_##msgnum, text } + +static kdbmsg_t kdbmsgs[] = { + KDBMSG(NOTFOUND,"Command Not Found"), + KDBMSG(ARGCOUNT, "Improper argument count, see usage."), + KDBMSG(BADWIDTH, "Illegal value for BYTESPERWORD use 1, 2 or 4"), + KDBMSG(BADRADIX, "Illegal value for RADIX use 8, 10 or 16"), + KDBMSG(NOTENV, "Cannot find environment variable"), + KDBMSG(NOENVVALUE, "Environment variable should have value"), + KDBMSG(NOTIMP, "Command not implemented"), + KDBMSG(ENVFULL, "Environment full"), + KDBMSG(ENVBUFFULL, "Environment buffer full"), + KDBMSG(TOOMANYBPT, "Too many breakpoints defined"), + KDBMSG(TOOMANYDBREGS, "More breakpoints than db registers defined"), + KDBMSG(DUPBPT, "Duplicate breakpoint address"), + KDBMSG(BPTNOTFOUND, "Breakpoint not found"), + KDBMSG(BADMODE, "IDMODE should be x86 or 8086"), + KDBMSG(BADINT, "Illegal numeric value"), + KDBMSG(INVADDRFMT, "Invalid symbolic address format"), + KDBMSG(BADREG, "Invalid register name"), + KDBMSG(BADCPUNUM, "Invalid cpu number"), + KDBMSG(BADLENGTH, "Invalid length field"), + KDBMSG(NOBP, "No Breakpoint exists"), +}; +#undef KDBMSG + +static const int __nkdb_err = sizeof(kdbmsgs) / sizeof(kdbmsg_t); + + +/* + * Initial environment. This is all kept static and local to + * this file. We don't want to rely on the memory allocation + * mechanisms in the kernel, so we use a very limited allocate-only + * heap for new and altered environment variables. The entire + * environment is limited to a fixed number of entries (add more + * to __env[] if required) and a fixed amount of heap (add more to + * KDB_ENVBUFSIZE if required). + */ + +static char *__env[] = { +#if defined(CONFIG_SMP) + "PROMPT=[%d]kdb> ", + "MOREPROMPT=[%d]more> ", +#else + "PROMPT=kdb> ", + "MOREPROMPT=more> ", +#endif + "RADIX=16", + "LINES=24", + "COLUMNS=80", + "MDCOUNT=8", /* lines of md output */ + "IDCOUNT=16", /* lines of id output */ + "BTARGS=5", /* 5 possible args in bt */ + "SSCOUNT=20", /* lines of ssb output */ + KDB_PLATFORM_ENV, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, +}; + +static const int __nenv = (sizeof(__env) / sizeof(char *)); + +/* + * kdbgetenv + * + * This function will return the character string value of + * an environment variable. + * + * Parameters: + * match A character string representing an environment variable. + * Outputs: + * None. + * Returns: + * NULL No environment variable matches 'match' + * char* Pointer to string value of environment variable. + * Locking: + * No locking considerations required. + * Remarks: + */ +char * +kdbgetenv(const char *match) +{ + char **ep = __env; + int matchlen = strlen(match); + int i; + + for(i=0; i<__nenv; i++) { + char *e = *ep++; + + if (!e) continue; + + if ((strncmp(match, e, matchlen) == 0) + && ((e[matchlen] == '\0') + ||(e[matchlen] == '='))) { + char *cp = strchr(e, '='); + return (cp)?++cp:""; + } + } + return (char *)0; +} + +/* + * kdballocenv + * + * This function is used to allocate bytes for environment entries. + * + * Parameters: + * match A character string representing a numeric value + * Outputs: + * *value the unsigned long represntation of the env variable 'match' + * Returns: + * Zero on success, a kdb diagnostic on failure. + * Locking: + * No locking considerations required. Must be called with all + * processors halted. + * Remarks: + * We use a static environment buffer (envbuffer) to hold the values + * of dynamically generated environment variables (see kdb_set). Buffer + * space once allocated is never free'd, so over time, the amount of space + * (currently 512 bytes) will be exhausted if env variables are changed + * frequently. + */ +static char * +kdballocenv(size_t bytes) +{ +#define KDB_ENVBUFSIZE 512 + static char envbuffer[KDB_ENVBUFSIZE]; + static int envbufsize = 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[] + * regs - Register state at time of KDB entry + * Outputs: + * *value - receives the value of the address-expression + * *offset - receives the offset specified, if any + * *name - receives the symbol name, if any + * *nextarg - index to next unparsed argument in argv[] + * + * Returns: + * zero is returned on success, a kdb diagnostic code is + * returned on error. + * + * Locking: + * No locking requirements. + * + * Remarks: + * + */ + +int +kdbgetaddrarg(int argc, const char **argv, int *nextarg, + unsigned long *value, long *offset, + char **name, kdb_eframe_t ef) +{ + unsigned long addr; + long off = 0; + int positive; + int diag; + int found = 0; + char *symname; + char symbol = '\0'; + char *cp; + kdb_symtab_t symtab; + + /* + * Process arguments which follow the following syntax: + * + * symbol | numeric-address [+/- numeric-offset] + * %register + * $environment-variable + */ + + if (*nextarg > argc) { + return KDB_ARGCOUNT; + } + + symname = (char *)argv[*nextarg]; + + /* + * If there is no whitespace between the symbol + * or address and the '+' or '-' symbols, we + * remember the character and replace it with a + * null so the symbol/value can be properly parsed + */ + if ((cp = strpbrk(symname, "+-")) != NULL) { + symbol = *cp; + *cp++ = '\0'; + } + + if (symname[0] == '$') { + diag = kdbgetulenv(&symname[1], &addr); + if (diag) + return diag; + } else if (symname[0] == '%') { + diag = kdba_getregcontents(&symname[1], ef, &addr); + if (diag) + return diag; + } else { + found = kdbgetsymval(symname, &symtab); + if (found) { + addr = symtab.sym_start; + } else { + diag = kdbgetularg(argv[*nextarg], &addr); + if (diag) + return diag; + } + } + + if (!found) + found = kdbnearsym(addr, &symtab); + + (*nextarg)++; + + if (name) + *name = symname; + if (value) + *value = addr; + if (offset && name && *name) + *offset = addr - symtab.sym_start; + + if ((*nextarg > argc) + && (symbol == '\0')) + return 0; + + /* + * check for +/- and offset + */ + + if (symbol == '\0') { + if ((argv[*nextarg][0] != '+') + && (argv[*nextarg][0] != '-')) { + /* + * Not our argument. Return. + */ + return 0; + } else { + positive = (argv[*nextarg][0] == '+'); + (*nextarg)++; + } + } else + positive = (symbol == '+'); + + /* + * Now there must be an offset! + */ + if ((*nextarg > argc) + && (symbol == '\0')) { + return KDB_INVADDRFMT; + } + + if (!symbol) { + cp = (char *)argv[*nextarg]; + (*nextarg)++; + } + + diag = kdbgetularg(cp, &off); + if (diag) + return diag; + + if (!positive) + off = -off; + + if (offset) + *offset += off; + + if (value) + *value += off; + + return 0; +} + +static void +kdb_cmderror(int diag) +{ + int i; + + if (diag >= 0) { + kdb_printf("no error detected\n"); + return; + } + + for(i=0; i<__nkdb_err; i++) { + if (kdbmsgs[i].km_diag == diag) { + kdb_printf("diag: %d: %s\n", diag, kdbmsgs[i].km_msg); + return; + } + } + + kdb_printf("Unknown diag %d\n", -diag); +} + +/* + * kdb_parse + * + * Parse the command line, search the command table for a + * matching command and invoke the command function. + * + * Parameters: + * cmdstr The input command line to be parsed. + * regs The registers at the time kdb was entered. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic if failure. + * Locking: + * None. + * Remarks: + * Limited to 20 tokens. + * + * Real rudimentary tokenization. Basically only whitespace + * is considered a token delimeter (but special consideration + * is taken of the '=' sign as used by the 'set' command). + * + * The algorithm used to tokenize the input string relies on + * there being at least one whitespace (or otherwise useless) + * character between tokens as the character immediately following + * the token is altered in-place to a null-byte to terminate the + * token string. + */ + +#define MAXARGC 20 + +static int +kdb_parse(char *cmdstr, kdb_eframe_t ef) +{ + char *argv[MAXARGC]; + int argc=0; + char *cp; + kdbtab_t *tp; + int i; + + /* + * First tokenize the command string. + */ + cp = cmdstr; + + /* + * If a null statement is provided, do nothing. + */ + if ((*cp == '\n') || (*cp == '\0')) + return 0; + + while (*cp) { + /* skip whitespace */ + while (isspace(*cp)) cp++; + if ((*cp == '\0') || (*cp == '\n')) + break; + argv[argc++] = cp; + /* Skip to next whitespace */ + for(; *cp && (!isspace(*cp) && (*cp != '=')); cp++); + *cp++ = '\0'; /* Squash a ws or '=' character */ + } + + 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, + ef); + } + + /* + * 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, ef)) { + return KDB_NOTFOUND; + } + + kdb_printf("%s = ", argv[0]); + kdb_symbol_print(value, NULL, KDB_SP_DEFAULT); + kdb_printf("\n"); + return 0; + } +} + +/* + * kdb_local + * + * The main code for kdb. This routine is invoked on a specific + * processor, it is not global. The main kdb() routine ensures + * that only one processor at a time is in this routine. This + * code is called with the real reason code on the first entry + * to a kdb session, thereafter it is called with reason SWITCH, + * even if the user goes back to the original cpu. + * + * Inputs: + * reason The reason KDB was invoked + * error The hardware-defined error code + * regs The registers at time of fault/breakpoint + * Returns: + * 0 KDB was invoked for an event which it wasn't responsible + * 1 KDB handled the event for which it was invoked. + * KDB_GO User typed 'go'. + * Single step pretends to be 'go' to release control to the OS. + * KDB_CPUSWITCH User switched to another cpu, kdb_new_cpu contains + * a valid cpu number. + * Locking: + * none + * Remarks: + * none + */ + +static int +kdb_local(kdb_reason_t reason, int error, kdb_eframe_t ef) +{ + char cmdbuf[255]; + char *cmd; + int diag; + kdb_dbtrap_t result=KDB_DB_NOBPT; + + if (reason == KDB_REASON_BREAK) + result = kdba_bp_trap(ef); /* Only call this once */ + if (reason == KDB_REASON_DEBUG) + result = kdba_db_trap(ef); /* Only call this once */ + + if ((reason == KDB_REASON_BREAK || reason == KDB_REASON_DEBUG) + && result == KDB_DB_NOBPT) { + return 0; /* Not one of mine */ + } + + if (reason != KDB_REASON_DEBUG) { + kdb_printf("Entering kdb (0x%p) ", current); +#if defined(CONFIG_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. + */ + switch(result) { + case KDB_DB_BPT: + kdb_printf("Entering kdb (0x%p) ", current); +#if defined(CONFIG_SMP) + kdb_printf("on processor %d ", smp_processor_id()); +#endif + kdb_printf("due to Debug @ 0x%lx\n", kdba_getpc(ef)); + break; + case KDB_DB_SSB: + /* + * In the midst of ssb command. Just return. + */ + return 1; /* We've handled this fault */ + + break; + case KDB_DB_SS: + break; + default: + kdb_printf("kdb: Bad result from kdba_db_trap: %d\n", + result); + break; + } + + } + 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"); + /* + * Get a set of registers that defines the current + * context (as of the call to kdb). + */ + kdb_getcurrentframe(ef); + kdba_setpc(ef, (long)(&kdb)); /* for traceback */ + break; + case KDB_REASON_PANIC: + kdb_printf("Panic: %s\n", kdb_diemsg); + kdb_printf("due to panic @ 0x%lx\n", kdba_getpc(ef)); + kdba_dumpregs(ef, NULL, NULL); + break; + case KDB_REASON_NMI: + kdb_printf("due to NonMaskable Interrupt @ 0x%lx\n", + kdba_getpc(ef)); + kdba_dumpregs(ef, NULL, NULL); + break; + case KDB_REASON_BREAK: + kdb_printf("due to Breakpoint @ 0x%lx\n", kdba_getpc(ef)); + /* + * Determine if this breakpoint is one that we + * are interested in. + */ + if (result != KDB_DB_BPT) { + kdb_printf("kdb: error return from kdba_bp_trap: %d\n", result); + return 0; /* Not for us, dismiss it */ + } + break; + default: + kdb_printf("kdb: unexpected reason code: %d\n", reason); + return 0; /* Not for us, dismiss it */ + } + + while (1) { + /* + * Initialize pager context. + */ + kdb_nextline = 1; + + /* + * Use kdb_setjmp/kdb_longjmp to break out of + * the pager early. + */ + if (kdb_setjmp(&kdbjmpbuf[smp_processor_id()])) { + /* + * Command aborted (usually in pager) + */ + + /* + * XXX - need to abort a SSB ? + */ + continue; + } + + /* + * Fetch command from keyboard + */ + cmd = kdb_getstr(cmdbuf, sizeof(cmdbuf), kdbgetenv("PROMPT")); + + diag = kdb_parse(cmd, ef); + if (diag == KDB_NOTFOUND) { + kdb_printf("Unknown kdb command: '%s'\n", cmd); + diag = 0; + } + /* ss[b] pretends to be KDB_GO */ + if ((diag == KDB_GO) + || (diag == KDB_CPUSWITCH)) + break; /* Go or cpu switch command */ + + if (diag) + kdb_cmderror(diag); + } + + return(diag); +} + +/* + * 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 + * Returns: + * 0 KDB was invoked for an event which it wasn't responsible + * 1 KDB handled the event for which it was invoked. + * Locking: + * none + * Remarks: + * No assumptions of system state. This function may be invoked + * with arbitrary locks held. It will stop all other processors + * in an SMP environment, disable all interrupts and does not use + * the operating systems keyboard driver. + * + * This code is reentrant but only for cpu switch. Any other + * reentrancy is an error. + * + * At the start of a kdb session the initial processor is running + * kdb() and the other processors can be doing anything. When the + * initial processor calls smp_kdb_stop() the other processors are + * driven through kdb_ipi which calls kdb() with reason SWITCH. + * That brings all processors into this routine, one with a "real" + * reason code, the other with SWITCH. + * + * Because the other processors are driven via smp_kdb_stop(), + * they enter here from the NMI handler. Until the other + * processors exit from here and exit from kdb_ipi, they will not + * take any more NMI requests. + */ + +int +kdb(kdb_reason_t reason, int error, kdb_eframe_t ef) +{ + kdb_intstate_t int_state; /* Interrupt state */ + kdb_reason_t reason2 = reason; + int result = 1; /* Default is kdb handled it */ + + /* + * If kdb is already active, print a message and continue. + * Sorry, you cannot use kdb to debug itself. + */ + if (reason != KDB_REASON_SWITCH) { + if (kdb_active) { + printk("kdb: debugger re-entered, allowing event to proceed\n"); + return(0); + } + } else if (!kdb_active) { + printk("kdb: CPU switch without kdb_active, I'm confused\n"); + return(0); + } + + /* + * Disable interrupts on this processor during kdb command processing + */ + kdba_disableint(&int_state); + kdb_bp_remove_local(); + kdba_disable_lbr(); + + if (kdb_flags & KDB_DEBUG_LBR) + kdba_print_lbr(); + + /* + * If not entering the debugger due to CPU switch, + * serialize access here. + * The processors may race getting to this point - if, + * for example, more than one processor hits a breakpoint + * at the same time. We'll serialize access to kdb here - + * other processors will loop here, and the NMI from the stop + * IPI will take them into kdb as switch candidates. Once + * the initial processor releases the debugger, the rest of + * the processors will race for it. + */ + if (reason != KDB_REASON_SWITCH) { + spin_lock(&kdb_lock); + + while (kdb_active) { + spin_unlock(&kdb_lock); + + while (kdb_active) + ; + + spin_lock(&kdb_lock); + } + + kdb_active = reason; + spin_unlock(&kdb_lock); + + /* + * Remove the global breakpoints. This is only done + * once from the initial processor. + */ + kdb_bp_remove_global(); + +#if defined(CONFIG_SMP) + /* + * If SMP, stop other processors. The other processors + * will enter kdb() with KDB_REASON_SWITCH and spin + * below. + */ + kdb_new_cpu = -1; + if (smp_num_cpus > 1) + smp_kdb_stop(); +#endif /* CONFIG_SMP */ + } + + /* Stay in kdb() until 'go' or an error */ + while (1) { +#ifdef CONFIG_SMP + /* + * All processors except the one that is in control + * will spin here. + */ + while (test_bit(smp_processor_id(), &smp_kdb_wait)) + ; + if (reason2 == KDB_REASON_SWITCH && kdb_new_cpu == -1) + break; /* Another cpu said 'go' */ +#endif + /* Still using kdb, this processor is in control */ + result = kdb_local(reason2, error, ef); + + /* Next entry to this processor must be for switch */ + reason2 = KDB_REASON_SWITCH; + +#if defined(CONFIG_SMP) + if (result == KDB_CPUSWITCH) { + /* Hold the current cpu, release the target one. */ + set_bit(smp_processor_id(), &smp_kdb_wait); + clear_bit(kdb_new_cpu, &smp_kdb_wait); + continue; + } +#endif + + /* + * All other return codes from kdb_local will end kdb(). + */ +#if defined(CONFIG_SMP) + kdb_new_cpu = -1; + smp_kdb_wait = 0; +#endif + break; + } + + if (reason != KDB_REASON_SWITCH) { + /* + * Install the global breakpoints. This is only done + * once from the initial processor. + */ + kdb_bp_install_global(ef); + kdb_active = 0; + } + + kdba_enable_lbr(); + kdb_bp_install_local(ef); + kdba_restoreint(&int_state); + + return(result != 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; + kdb_symtab_t symtab; + int diag; + int nextarg; + int nosect = 0; + 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 = sizeof(long); + + 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; + } + + kdbgetintenv("BYTESPERWORD", &width); + kdbgetintenv("NOSECT", &nosect); + + if (strcmp(argv[0], "mds") == 0) { + symbolic = 1; + width = 4; + } + + switch (width) { + case 8: + sprintf(fmtstr, "%%16.16%c ", fmtchar); + break; + 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 = kdba_getword(addr, sizeof(word)); + if (kdb_flags & KDB_FLAG_SUPRESS) { + kdb_flags &= ~KDB_FLAG_SUPRESS; + return 0; + } + + diag = kdba_putword(addr, sizeof(contents), contents); + + kdb_printf("0x%lx = 0x%lx\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, kdb_eframe_t ef) +{ + unsigned long addr; + int diag; + int nextarg; + long offset; + + if (argc == 1) { + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, + &addr, &offset, NULL, ef); + if (diag) + return diag; + + kdba_setpc(ef, addr); + } else if (argc) + return KDB_ARGCOUNT; + + return KDB_GO; +} + +/* + * kdb_rd + * + * This function implements the 'rd' command. + * + * rd display all general registers. + * rd c display all control registers. + * rd d display all debug registers. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_rd(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + /* + */ + + if (argc == 0) { + return kdba_dumpregs(regs, NULL, NULL); + } + + if (argc > 2) { + return KDB_ARGCOUNT; + } + + return kdba_dumpregs(regs, argv[1], argv[2]); +} + +/* + * kdb_rm + * + * This function implements the 'rm' (register modify) command. + * + * rm register-name new-contents + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Currently doesn't allow modification of control or + * debug registers, nor does it allow modification + * of model-specific registers (MSR). + */ + +int +kdb_rm(int argc, const char **argv, const char **envp, kdb_eframe_t ef) +{ + int diag; + int ind = 0; + 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 = kdba_setregcontents(&argv[1][ind], ef, contents); + if (diag) + return diag; + + return 0; +} + +#if defined(CONFIG_MAGIC_SYSRQ) +/* + * kdb_sr + * + * This function implements the 'sr' (SYSRQ key) command which + * interfaces to the soi-disant MAGIC SYSRQ functionality. + * + * sr + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * None. + */ +int +kdb_sr(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + if (argc != 1) { + return KDB_ARGCOUNT; + } + + handle_sysrq(*argv[1], regs, 0, 0); + + return 0; +} +#endif /* CONFIG_MAGIC_SYSRQ */ + +/* + * kdb_ef + * + * This function implements the 'ef' (display exception frame) + * command. This command takes an address and expects to find + * an exception frame at that address, formats and prints it. + * + * ef address-expression + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Not done yet. + */ + +int +kdb_ef(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int diag; + kdb_machreg_t addr; + long offset; + int nextarg; + + if (argc == 1) { + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + return kdba_dumpregs((struct pt_regs *)addr, NULL, NULL); + } + + return KDB_ARGCOUNT; +} + +/* + * kdb_reboot + * + * This function implements the 'reboot' command. Reboot the system + * immediately. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Shouldn't return from this function. + */ + +int +kdb_reboot(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + machine_restart(0); + /* NOTREACHED */ + return 0; +} + +/* + * kdb_env + * + * This function implements the 'env' command. Display the current + * environment variables. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_env(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + + for(i=0; i<__nenv; i++) { + if (__env[i]) { + kdb_printf("%s\n", __env[i]); + } + } + + if (kdb_flags & KDB_DEBUG_MASK) + kdb_printf("KDBFLAGS=0x%x\n", kdb_flags); + + return 0; +} + +/* + * kdb_set + * + * This function implements the 'set' command. Alter an existing + * environment variable or create a new one. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_set(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + char *ep; + size_t varlen, vallen; + + /* + * we can be invoked two ways: + * set var=value argv[1]="var", argv[2]="value" + * set var = value argv[1]="var", argv[2]="=", argv[3]="value" + * - if the latter, shift 'em down. + */ + if (argc == 3) { + argv[2] = argv[3]; + argc--; + } + + if (argc != 2) + return KDB_ARGCOUNT; + + /* + * Check for internal variables + */ + if (strcmp(argv[1], "KDBDEBUG") == 0) { + unsigned int debugflags; + char *cp; + + debugflags = simple_strtoul(argv[2], &cp, 0); + if (cp == argv[2]) { + kdb_printf("kdb: illegal debug flags '%s'\n", + argv[2]); + return 0; + } + kdb_flags = (kdb_flags & ~KDB_DEBUG_MASK) + | ((debugflags & 0xff) << KDB_DEBUG_SHIFT); + + return 0; + } + + /* + * Tokenizer squashed the '=' sign. argv[1] is variable + * name, argv[2] = value. + */ + varlen = strlen(argv[1]); + vallen = strlen(argv[2]); + ep = kdballocenv(varlen + vallen + 2); + if (ep == (char *)0) + return KDB_ENVBUFFULL; + + sprintf(ep, "%s=%s", argv[1], argv[2]); + + ep[varlen+vallen+1]='\0'; + + for(i=0; i<__nenv; i++) { + if (__env[i] + && ((strncmp(__env[i], argv[1], varlen)==0) + && ((__env[i][varlen] == '\0') + || (__env[i][varlen] == '=')))) { + __env[i] = ep; + return 0; + } + } + + /* + * Wasn't existing variable. Fit into slot. + */ + for(i=0; i<__nenv-1; i++) { + if (__env[i] == (char *)0) { + __env[i] = ep; + return 0; + } + } + + return KDB_ENVFULL; +} + +#if defined(CONFIG_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 /* CONFIG_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 State Thread Command\n"); + for_each_task(p) { + kdb_printf("0x%p %08d %08d %1.1d %3.3d %s 0x%p%c%s\n", + p, p->pid, p->p_pptr->pid, + p->has_cpu, p->processor, + (p->state == 0)?"run ":(p->state>0)?"stop":"unrn", + &p->thread, + (p == current) ? '*': ' ', + p->comm); + } + + return 0; +} + +/* + * kdb_ll + * + * This function implements the 'll' command which follows a linked + * list and executes an arbitrary command for each element. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_ll(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int diag; + 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 = kdba_getword(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_t func, + char *usage, + char *help, + short minlen) +{ + int i; + kdbtab_t *kp; + + /* + * Brute force method to determine duplicates + */ + for (i=0, kp=kdb_commands; icmd_name && (strcmp(kp->cmd_name, cmd)==0)) { + kdb_printf("Duplicate kdb command registered: '%s'\n", + cmd); + return 1; + } + } + + /* + * Insert command into first available location in table + */ + for (i=0, kp=kdb_commands; icmd_name == NULL) { + kp->cmd_name = cmd; + kp->cmd_func = func; + kp->cmd_usage = usage; + kp->cmd_help = help; + kp->cmd_flags = 0; + kp->cmd_minlen = minlen; + break; + } + } + return 0; +} + +/* + * kdb_unregister + * + * This function is used to unregister a kernel debugger command. + * It is generally called when a module which implements kdb + * commands is unloaded. + * + * Inputs: + * cmd Command name + * Outputs: + * None. + * Returns: + * zero for success, one command not registered. + * Locking: + * none. + * Remarks: + * + */ + +int +kdb_unregister(char *cmd) +{ + int i; + kdbtab_t *kp; + + /* + * find the command. + */ + for (i=0, kp=kdb_commands; icmd_name && (strcmp(kp->cmd_name, cmd)==0)) { + kp->cmd_name = NULL; + return 0; + } + } + + /* + * Couldn't find it. + */ + return 1; +} + +/* + * kdb_inittab + * + * This function is called by the kdb_init function to initialize + * the kdb command table. It must be called prior to any other + * call to kdb_register. + * + * Inputs: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + */ + +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("bta", kdb_bt, "", "Display stack all processes", 0); + kdb_register("ll", kdb_ll, " ", "Execute cmd for each element in linked list", 0); + kdb_register("env", kdb_env, "", "Show environment variables", 0); + kdb_register("set", kdb_set, "", "Set environment variables", 0); + kdb_register("help", kdb_help, "", "Display Help Message", 1); + kdb_register("?", kdb_help, "", "Display Help Message", 0); +#if defined(CONFIG_SMP) + kdb_register("cpu", kdb_cpu, "","Switch to new cpu", 0); +#endif /* CONFIG_SMP */ + kdb_register("ps", kdb_ps, "", "Display active task list", 0); + kdb_register("reboot", kdb_reboot, "", "Reboot the machine immediately", 0); +#if defined(CONFIG_MAGIC_SYSRQ) + kdb_register("sr", kdb_sr, "", "Magic SysRq key", 0); +#endif +} + +/* + * kdb_init + * + * Initialize the kernel debugger environment. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +void +kdb_init(void) +{ + /* + * This must be called before any calls to kdb_printf. + */ + kdb_io_init(); + + kdb_inittab(); /* Initialize Command Table */ + kdb_initbptab(); /* Initialize Breakpoint Table */ + kdb_id_init(); /* Initialize Disassembler */ + kdba_init(); /* Architecture Dependent Initialization */ + + /* + * Use printk() to get message in log_buf[]; + */ + printk("kdb version %d.%d by Scott Lurndal. "\ + "Copyright SGI, All Rights Reserved\n", + KDB_MAJOR_VERSION, KDB_MINOR_VERSION); +} diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/kdb/kdbsupport.c linux/kdb/kdbsupport.c --- /opt/kernel/linux-2.4.0-test1-ac21/kdb/kdbsupport.c Thu Jan 1 01:00:00 1970 +++ linux/kdb/kdbsupport.c Tue Jun 13 16:30:11 2000 @@ -0,0 +1,460 @@ +/* + * Kernel Debugger Architecture Independent Support Functions + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Symbol table functions. + */ + +/* + * kdbgetsymval + * + * Return the address of the given symbol. + * + * Parameters: + * symname Character string containing symbol name + * symtab Structure to receive results + * Outputs: + * Returns: + * 0 Symbol not found, symtab zero filled + * 1 Symbol mapped to module/symbol/section, data in symtab + * Locking: + * None. + * Remarks: + */ + +int +kdbgetsymval(const char *symname, kdb_symtab_t *symtab) +{ + memset(symtab, 0, sizeof(*symtab)); + return(kallsyms_symbol_to_address( + symname, + NULL, + &symtab->mod_name, + &symtab->mod_start, + &symtab->mod_end, + &symtab->sec_name, + &symtab->sec_start, + &symtab->sec_end, + &symtab->sym_name, + &symtab->sym_start, + &symtab->sym_end)); +} + +/* + * kdbnearsym + * + * Return the name of the symbol with the nearest address + * less than 'addr'. + * + * Parameters: + * addr Address to check for symbol near + * symtab Structure to receive results + * Outputs: + * Returns: + * 0 No sections contain this address, symtab zero filled + * 1 Address mapped to module/symbol/section, data in symtab + * Locking: + * None. + * Remarks: + */ + +int +kdbnearsym(unsigned long addr, kdb_symtab_t *symtab) +{ + memset(symtab, 0, sizeof(*symtab)); + return(kallsyms_address_to_symbol( + addr, + &symtab->mod_name, + &symtab->mod_start, + &symtab->mod_end, + &symtab->sec_name, + &symtab->sec_start, + &symtab->sec_end, + &symtab->sym_name, + &symtab->sym_start, + &symtab->sym_end)); +} + +#if defined(CONFIG_SMP) +/* + * kdb_ipi + * + * This function is called from the non-maskable interrupt + * handler to handle a kdb IPI instruction. + * + * Inputs: + * ef = Exception frame pointer + * Outputs: + * None. + * Returns: + * 0 - Did not handle NMI + * 1 - Handled NMI + * Locking: + * None. + * Remarks: + * Initially one processor is invoked in the kdb() code. That + * processor sends an ipi which drives this routine on the other + * processors. All this does is call kdb() with reason SWITCH. + * This puts all processors into the kdb() routine and all the + * code for breakpoints etc. is in one place. + */ + +int +kdb_ipi(kdb_eframe_t ef, void (*ack_interrupt)(void)) +{ + if (kdb_active + && test_bit(smp_processor_id(), &smp_kdb_wait)) { + /* + * Stopping other processors via smp_kdb_stop(). + */ + if (ack_interrupt) + (*ack_interrupt)(); /* Acknowledge the interrupt */ + kdb(KDB_REASON_SWITCH, 0, ef); /* Spin in kdb() */ + return 1; + } + return 0; +} +#endif /* CONFIG_SMP */ + +void +kdb_enablehwfault(void) +{ + kdba_enable_mce(); +} + +/* + * kdb_get_next_ar + * + * Get the next activation record from the stack. + * + * Inputs: + * arend Last byte +1 of the activation record. sp for the first + * frame, start of callee's activation record otherwise. + * func Start address of function. + * pc Current program counter within this function. pc for + * the first frame, caller's return address otherwise. + * fp Current frame pointer. Register fp for the first + * frame, oldfp otherwise. 0 if not known. + * ss Start of stack for the current process. + * Outputs: + * ar Activation record. + * symtab kallsyms symbol table data for the calling function. + * Returns: + * 1 if ar is usable, 0 if not. + * Locking: + * None. + * Remarks: + * Activation Record format, assuming a stack that grows down + * (KDB_STACK_DIRECTION == -1). + * + * +-----------------------------+ ^ ===================== + * | Return address, frame 3 | | + * +-----------------------------+ | + * | Frame Pointer, frame 3 |>--' + * +-----------------------------+<--. + * | Locals and automatics, | | + * | frame 2. (variable size) | | AR 2 + * +-----------------------------+ | + * | Save registers, | | + * | frame 2. (variable size) | | + * +-----------------------------+ | + * | Arguments to frame 1, | | + * | (variable size) | | + * +-----------------------------+ | ===================== + * | Return address, frame 2 | | + * +-----------------------------+ | + * | Frame Pointer, frame 2 |>--' + * +-----------------------------+<--. + * | Locals and automatics, | | + * | frame 1. (variable size) | | AR 1 + * +-----------------------------+ | + * | Save registers, | | + * | frame 1. (variable size) | | + * +-----------------------------+ | + * | Arguments to frame 0, | | + * | (variable size) | | + * +-----------------------------+ | -- (5) ===================== + * | Return address, frame 1 | | + * +-----------------------------+ | -- (0) + * | Frame Pointer, frame 1 |>--' + * +-----------------------------+ -- (1), (2) + * | Locals and automatics, | + * | frame 0. (variable size) | AR 0 + * +-----------------------------+ -- (3) + * | Save registers, | + * | frame 0. (variable size) | + * +-----------------------------+ -- (4) ===================== + * + * The stack for the top frame can be in one of several states. + * (0) Immediately on entry to the function, stack pointer (sp) is + * here. + * (1) If the function was compiled with frame pointers and the 'push + * fp' instruction has been executed then the pointer to the + * previous frame is on the stack. However there is no guarantee + * that this saved pointer is valid, the calling function might + * not have frame pointers. sp is adjusted by wordsize after + * 'push fp'. + * (2) If the function was compiled with frame pointers and the 'copy + * sp to fp' instruction has been executed then fp points here. + * (3) If the function startup has 'adjust sp by 0xnn bytes' and that + * instruction has been executed then sp has been adjusted by + * 0xnn bytes for local and automatic variables. + * (4) If the function startup has one or more 'push reg' instructions + * and any have been executed then sp has been adjusted by + * wordsize bytes for each register saved. + * + * As the function exits it rewinds the stack, typically to (1) then (0). + * + * The stack entries for the lower frames is normally are in state (5). + * (5) Arguments for the called frame are on to the stack. + * However lower frames can be incomplete if there is an interrupt in + * progress. + * + * An activation record runs from the return address for a function + * through to the return address for the next function or sp, whichever + * comes first. For each activation record we extract :- + * + * start Address of the activation record. + * end Address of the last byte+1 in the activation record. + * ret Return address to caller. + * oldfp Frame pointer to previous frame, 0 if this function was + * not compiled with frame pointers. + * fp Frame pointer for the current frame, 0 if this function + * was not compiled with frame pointers or fp has not been + * set yet. + * arg0 Address of the first argument (in the previous activation + * record). + * locals Bytes allocated to locals and automatics. + * regs Bytes allocated to saved registers. + * args Bytes allocated to arguments (in the previous activation + * record). + * setup Bytes allocated to setup data on stack (return address, + * frame pointer). + * + * Although the kernel might be compiled with frame pointers, we still + * have to assume the worst and validate the frame. Some calls from + * asm code to C code might not use frame pointers. Third party binary + * only modules might be compiled without frame pointers, even when the + * rest of the kernel has frame pointers. Some routines are always + * compiled with frame pointers, even if the overall kernel is not. A + * routine compiled with frame pointers can be called from a routine + * without frame pointers, the previous "frame pointer" is saved on + * stack but it contains garbage. + * + * We check the object code to see if it saved a frame pointer and we + * validate that pointer. Basically frame pointers are hints. + */ + +#define FORCE_ARG(ar,n) (ar)->setup = (ar)->locals = (ar)->regs = \ + (ar)->fp = (ar)->oldfp = (ar)->ret = 0; \ + (ar)->start = (ar)->end - KDB_STACK_DIRECTION*(n)*sizeof(unsigned long); + +int +kdb_get_next_ar(kdb_machreg_t arend, kdb_machreg_t func, + kdb_machreg_t pc, kdb_machreg_t fp, kdb_machreg_t ss, + kdb_ar_t *ar, kdb_symtab_t *symtab) +{ + if (kdb_flags & KDB_DEBUG_AR) { + kdb_printf("kdb_get_next_ar: arend=0x%lx func=0x%lx pc=0x%lx fp=0x%lx\n", + arend, func, pc, fp); + } + + memset(ar, 0, sizeof(*ar)); + if (!kdbnearsym(pc, symtab)) { + symtab->sym_name = ""; + symtab->sym_start = 0; + if (kdb_flags & KDB_DEBUG_AR) { + kdb_printf("kdb_get_next_ar: callee not in kernel\n"); + } + return(0); + } + + if (!kdba_prologue(symtab, pc, arend, fp, ss, 0, ar)) { + if (kdb_flags & KDB_DEBUG_AR) { + kdb_printf("kdb_get_next_ar: callee prologue failed\n"); + } + return(0); + } + if (kdb_flags & KDB_DEBUG_AR) { + kdb_printf("kdb_get_next_ar: callee activation record\n"); + kdb_printf(" start=0x%lx end=0x%lx ret=0x%lx oldfp=0x%lx fp=0x%lx\n", + ar->start, ar->end, ar->ret, ar->oldfp, ar->fp); + kdb_printf(" locals=%d regs=%d setup=%d\n", + ar->locals, ar->regs, ar->setup); + } + + if (ar->ret) { + /* Run the caller code to get arguments to callee function */ + kdb_symtab_t caller_symtab; + kdb_ar_t caller_ar; + memset(&caller_ar, 0, sizeof(caller_ar)); + if (!kdbnearsym(ar->ret, &caller_symtab)) { + if (kdb_flags & KDB_DEBUG_AR) { + kdb_printf("kdb_get_next_ar: caller not in kernel\n"); + } + } else if (kdba_prologue(&caller_symtab, ar->ret, + ar->start, ar->oldfp, ss, 1, &caller_ar)) { + /* some caller data extracted */ ; + } else if (strcmp(symtab->sym_name, "do_exit") == 0) { + /* non-standard caller, force one argument */ + FORCE_ARG(&caller_ar, 1); + } else if (kdb_flags & KDB_DEBUG_AR) { + kdb_printf("kdb_get_next_ar: caller prologue failed\n"); + } + if (kdb_flags & KDB_DEBUG_AR) { + kdb_printf("kdb_get_next_ar: caller activation record\n"); + kdb_printf(" start=0x%lx end=0x%lx ret=0x%lx" + " oldfp=0x%lx fp=0x%lx\n", + caller_ar.start, caller_ar.end, caller_ar.ret, + caller_ar.oldfp, caller_ar.fp); + kdb_printf(" locals=%d regs=%d args=%d setup=%d\n", + caller_ar.locals, caller_ar.regs, + caller_ar.args, caller_ar.setup); + } + if (caller_ar.start) { + ar->args = KDB_STACK_DIRECTION*(caller_ar.end - caller_ar.start) - + (caller_ar.setup + caller_ar.locals + caller_ar.regs); + if (ar->args < 0) + ar->args = 0; + if (ar->args) { + ar->arg0 = ar->start - + KDB_STACK_DIRECTION*(ar->args - 4); + if (kdb_flags & KDB_DEBUG_AR) { + kdb_printf(" callee arg0=0x%lx args=%d\n", + ar->arg0, ar->args); + } + } + } + } + + return(1); +} + +/* + * kdb_dis_fprintf_dummy + * + * A dummy printf function for the disassembler, it does nothing. + * This lets code call the disassembler to step through + * instructions without actually printing anything. + * Inputs: + * Always ignored. + * Outputs: + * None. + * Returns: + * Always 0. + * Locking: + * none. + * Remarks: + * None. + */ + +int +kdb_dis_fprintf_dummy(FILE* file, const char *fmt, ...) +{ + return(0); +} + +/* + * kdb_symbol_print + * + * Standard method for printing a symbol name and offset. + * Inputs: + * addr Address to be printed. + * symtab Address of symbol data, if NULL this routine does its + * own lookup. + * punc Punctuation for string, bit field. + * Outputs: + * None. + * Returns: + * Always 0. + * Locking: + * none. + * Remarks: + * The string and its punctuation is only printed if the address + * is inside the kernel, except that the value is always printed + * when requested. + */ + +void +kdb_symbol_print(kdb_machreg_t addr, const kdb_symtab_t *symtab_p, unsigned int punc) +{ + kdb_symtab_t symtab, *symtab_p2; + if (symtab_p) { + symtab_p2 = (kdb_symtab_t *)symtab_p; + } + else { + symtab_p2 = &symtab; + kdbnearsym(addr, symtab_p2); + } + if (symtab_p2->sym_name || (punc & KDB_SP_VALUE)) { + ; /* drop through */ + } + else { + return; + } + if (punc & KDB_SP_SPACEB) { + kdb_printf(" "); + } + if (punc & KDB_SP_VALUE) { + kdb_printf("0x%0*lx", 2*sizeof(addr), addr); + } + if (!symtab_p2->sym_name) { + return; + } + if (punc & KDB_SP_VALUE) { + kdb_printf(" "); + } + if (punc & KDB_SP_PAREN) { + kdb_printf("("); + } + if (strcmp(symtab_p2->mod_name, "kernel")) { + kdb_printf("[%s]", symtab_p2->mod_name); + } + kdb_printf("%s", symtab_p2->sym_name); + if (addr != symtab_p2->sym_start) { + kdb_printf("+0x%lx", addr - symtab_p2->sym_start); + } + if (punc & KDB_SP_SYMSIZE) { + kdb_printf("/0x%lx", symtab_p2->sym_end - symtab_p2->sym_start); + } + if (punc & KDB_SP_PAREN) { + kdb_printf(")"); + } + if (punc & KDB_SP_SPACEA) { + kdb_printf(" "); + } + if (punc & KDB_SP_NEWLINE) { + kdb_printf("\n"); + } +} diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/kdb/modules/Makefile linux/kdb/modules/Makefile --- /opt/kernel/linux-2.4.0-test1-ac21/kdb/modules/Makefile Thu Jan 1 01:00:00 1970 +++ linux/kdb/modules/Makefile Tue Jun 13 16:30:11 2000 @@ -0,0 +1,23 @@ +# +# 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) + +M_OBJS := kdbm_vm.o + +ifeq ($(CONFIG_PAGE_BUF),y) +M_OBJS += kdbm_pb.o +endif +ifeq ($(CONFIG_PAGE_BUF),m) +M_OBJS += kdbm_pb.o +endif + + +include $(TOPDIR)/Rules.make diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/kdb/modules/kdbm_pb.c linux/kdb/modules/kdbm_pb.c --- /opt/kernel/linux-2.4.0-test1-ac21/kdb/modules/kdbm_pb.c Thu Jan 1 01:00:00 1970 +++ linux/kdb/modules/kdbm_pb.c Tue Jun 13 16:30:11 2000 @@ -0,0 +1,653 @@ +#define _PAGE_BUF_INTERNAL_ 1 +#include +#include +#include +#include +#include +#include +#include + +static char *pb_flag_vals[] = { + "READ", "WRITE", "MAPPED", "PARTIAL", + "ASYNC", "NONE", "DELWRI", "FREED", "SYNC", + "LONG_TERM", "FS_RESERVED_1", "FS_RESERVED_2", "RELEASE", + "LOCK", "TRYLOCK", "ALLOCATE", "FILE_ALLOCATE", "DONT_BLOCK", + "DIRECT", "LOCKABLE", "NEXT_KEY", "ENTER_PAGES", + "ALL_PAGES_MAPPED", "SOME_INVALID_PAGES", "ADDR_ALLOCATED", + "MEM_ALLOCATED", "GRIO", "FORECIO", + NULL }; + +static char *pg_flag_vals[] = { + "PG_locked", "PG_error", "PG_referenced", "PG_uptodate", "", + "PG_decr_after", "", "", + "PG_Slab", "PG_swap_cache", "PG_skip", "PG_swap_entry", + "PG_highmem", "PG_delalloc", "PG_partial", + "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", + "PG_partial", NULL }; + +static char *bh_state_vals[] = { + "Uptodate", "Dirty", "Lock", "Req", "Mapped", "New", + "Protected", NULL }; + +static char *pbm_flag_vals[] = { + "EOF", "HOLE", "DELAY", "FLUSH_OVERLAPS", + "READAHEAD", "UNWRITTEN", "DONTALLOC", "NEW", + NULL }; + +static char *map_flags(unsigned long flags, char *mapping[]) +{ + static char buffer[256]; + int index; + int offset = 12; + + buffer[0] = '\0'; + + for (index = 0; flags && mapping[index]; flags >>= 1, index++) { + if (flags & 1) { + if ((offset + strlen(mapping[index]) + 1) >= 80) { + strcat(buffer, "\n "); + offset = 12; + } else if (offset > 12) { + strcat(buffer, " "); + offset++; + } + strcat(buffer, mapping[index]); + offset += strlen(mapping[index]); + } + } + + return (buffer); +} + +static char *pb_flags(page_buf_flags_t pb_flag) +{ + return(map_flags((unsigned long) pb_flag, pb_flag_vals)); +} + +static char *page_flags(unsigned long flags) +{ + return(map_flags(flags, pg_flag_vals)); +} + + +int +kdbm_pb_flags(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + unsigned long flags; + int diag; + + if (argc != 1) + return KDB_ARGCOUNT; + + diag = kdbgetularg(argv[1], &flags); + if (diag) + return diag; + + kdb_printf("pb flags 0x%lx = %s\n", flags, pb_flags(flags)); + + return 0; +} + +int +kdbm_pb(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + page_buf_private_t bp; + unsigned char *p = (unsigned char *)&bp; + unsigned long addr; + long offset=0; + int nextarg; + int diag; + int i; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; ipages; + + kdb_printf(" page_struct index cnt flags\n"); + + while (curr != head) { + struct page page; + struct list_head curr_struct; + + addr = (unsigned long) list_entry(curr, struct page, list); + p = (unsigned char *)&page; + + for (j = 0; j < sizeof(struct page); j++) + *p++ = kdba_getword(addr+j, 1); + + if (!addr1 || page.index == addr1) { + kdb_printf(" 0x%lx 0x%lx %d 0x%lx ", + addr, page.index, page.count.counter, + page.flags); + } + if (page.buffers) { + do_buffer((unsigned long) page.buffers); + } else + kdb_printf("UNMAPPED\n"); + + addr = (unsigned long) curr; + p = (unsigned char *)&curr_struct; + for (j = 0; j < sizeof(struct list_head); j++) + *p++ = kdba_getword(addr+j, 1); + + curr = curr_struct.next; + } + + return 0; +} + +int +kdbm_inode(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + struct inode inode; + unsigned char *p = (unsigned char *)&inode; + unsigned long addr; + long offset=0; + int nextarg; + int diag; + int i; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i = 0; i < sizeof(struct inode); i++) { + *p++ = kdba_getword(addr+i, 1); + } + + kdb_printf("struct inode at 0x%lx\n", addr); + + kdb_printf(" i_ino = %lu i_count = %u i_dev = 0x%x i_size %Ld\n", + inode.i_ino, inode.i_count, + inode.i_dev, inode.i_size); + + kdb_printf(" i_mode = 0x%x i_nlink = %d i_rdev = 0x%x\n", + inode.i_mode, inode.i_nlink, + inode.i_rdev); + + kdb_printf(" i_hash.nxt = 0x%p i_hash.prv = 0x%p\n", + inode.i_hash.next, inode.i_hash.prev); + + kdb_printf(" i_list.nxt = 0x%p i_list.prv = 0x%p\n", + inode.i_list.next, inode.i_list.prev); + + kdb_printf(" i_dentry.nxt = 0x%p i_dentry.prv = 0x%p\n", + inode.i_dentry.next, + inode.i_dentry.prev); + + kdb_printf(" i_sb = 0x%p i_op = 0x%p i_data = 0x%lx nrpages = %lu\n", + inode.i_sb, inode.i_op, + addr + offsetof(struct inode, i_data), + inode.i_data.nrpages); + + kdb_printf(" vnode ptr 0x%p\n", inode.u.xfs_i.vnodep); + + return (0); +} + +int +kdbm_kiobuf(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + struct kiobuf kiobuf; + struct page page; + unsigned char *p = (unsigned char *)&kiobuf; + struct page *page_array[64]; + unsigned long addr; + long offset=0; + int nextarg; + int diag; + int i, j; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; imap_array)) { + p = (char *)page_array; + for (i=0; i < (kiobuf.nr_pages * sizeof(struct page *)); i++) { + *p++ = kdba_getword((unsigned)kiobuf.maplist + i, 1); + } + kiobuf.maplist = page_array; + } + kdb_printf(" page_struct page_addr cnt flags\n"); + for (i = 0; i < kiobuf.nr_pages; i++) { + addr = (unsigned long) kiobuf.maplist[i]; + p = (unsigned char *)&page; + + for (j = 0; j < sizeof(struct page); j++) { + *p++ = kdba_getword(addr+j, 1); + } + kdb_printf(" 0x%p 0x%lx %d 0x%lx\n", + kiobuf.maplist[i], page.virtual, + page.count.counter, page.flags); + } + + return (0); +} + +#ifdef PAGEBUF_TRACE +#include + +#define EV_SIZE (sizeof(event_names)/sizeof(char *)) + +int +kdbm_pbtrace(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + extern struct pagebuf_trace_buf pb_trace; + unsigned long match = 0; + int diag; + int i, total, end; + pagebuf_trace_t *trace; + char *event; + char value[10]; + + if (argc > 1) + return KDB_ARGCOUNT; + + if (argc == 1) { + diag = kdbgetularg(argv[1], &match); + if (diag) + return diag; + } + + end = pb_trace.start - 1; + if (end < 0) + end = PB_TRACE_BUFSIZE - 1; + + if ((argc == 1) && (match < PB_TRACE_BUFSIZE)) { + for (i = pb_trace.start, total = 0; i != end; i = CIRC_INC(i)) { + trace = &pb_trace.buf[i]; + if (trace->pb == 0) + continue; + total++; + } + total = total - match; + for (i = pb_trace.start; i != end && total; i = CIRC_INC(i)) { + trace = &pb_trace.buf[i]; + if (trace->pb == 0) + continue; + total--; + } + match = 0; + } else + i = pb_trace.start; + for ( ; i != end; i = CIRC_INC(i)) { + trace = &pb_trace.buf[i]; + + if (trace->pb == 0) + continue; + + if ((match != 0) && (trace->pb != match)) + continue; + + if ((trace->event < EV_SIZE) && event_names[trace->event]) { + event = event_names[trace->event]; + } else if (trace->event == 1000) { + event = (char *)trace->misc; + } else { + event = value; + sprintf(value, "%8d", trace->event); + } + + + kdb_printf("pb 0x%lx [%s] (hold %u lock %d) misc 0x%p", + trace->pb, event, + trace->hold, trace->lock_value, + trace->misc); + kdb_symbol_print((unsigned int)trace->ra, NULL, + KDB_SP_SPACEB|KDB_SP_PAREN|KDB_SP_NEWLINE); + kdb_printf(" offset 0x%Lx size 0x%x task 0x%p\n", + trace->offset, trace->size, trace->task); + kdb_printf(" flags: %s\n", + pb_flags(trace->flags)); + } + + return 0; + +} + +#else +int +kdbm_pbtrace(int argc, const char **argv, const char **envp, + struct pt_regs *regs) +{ + kdb_printf("pagebuf tracing not enabled\n"); + + return 0; +} +#endif + +int +init_module(void) +{ + kdb_register("pb", kdbm_pb, "", "Display page_buf_t", 0); + kdb_register("pbtrace", kdbm_pbtrace, "", "page_buf_t trace", 0); + kdb_register("pbflags", kdbm_pb_flags, "", "Display page buf flags", 0); + kdb_register("kiobuf", kdbm_kiobuf, "", "Display kiobuf", 0); + kdb_register("page", kdbm_page, "", "Display page", 0); + kdb_register("inode", kdbm_inode, "", "Display inode", 0); + kdb_register("bh", kdbm_buffers, "", "Display buffer", 0); + kdb_register("inode_pages", kdbm_inode_pages, "", "Display pages in an inode", 0); + kdb_register("pbiodesc", kdbm_pbiodesc, "", "Display I/O Descriptor", 0); + kdb_register("pbmap", kdbm_pbmap, "", "Display Bmap", 0); + +#ifdef PAGEBUF_TRACE + { + extern int debug_pagebuf; + + debug_pagebuf = 1; + } +#endif + + return 0; +} + +void +cleanup_module(void) +{ + kdb_unregister("pb"); + kdb_unregister("pbtrace"); + kdb_unregister("pbflags"); + kdb_unregister("kiobuf"); + kdb_unregister("page"); + kdb_unregister("inode"); + kdb_unregister("bh"); + kdb_unregister("pbiodesc"); + kdb_unregister("pbmap"); + kdb_unregister("inode_pages"); +} diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/kdb/modules/kdbm_vm.c linux/kdb/modules/kdbm_vm.c --- /opt/kernel/linux-2.4.0-test1-ac21/kdb/modules/kdbm_vm.c Thu Jan 1 01:00:00 1970 +++ linux/kdb/modules/kdbm_vm.c Mon Jun 19 02:31:46 2000 @@ -0,0 +1,660 @@ +#include +#include +#include +#include +#include +#include + +#include <../drivers/scsi/scsi.h> +#include <../drivers/scsi/hosts.h> + +/* The request printing worked under 2.3.42 but not 2.3.99-pre2. + * Feel free to rework it for 2.3.99-pre2 and submit a patch to SGI. + * Look for DO_REQUEST_PRINTING, remove the #ifdef and this comment + * when you get it working. Keith Owens + */ + +struct __vmflags { + unsigned long mask; + char *name; +} vmflags[] = { + { VM_READ, "READ" }, + { VM_WRITE, "WRITE" }, + { VM_EXEC, "EXEC" }, + { VM_SHARED, "SHARED" }, + { VM_MAYREAD, "MAYREAD" }, + { VM_MAYWRITE, "MAYWRITE" }, + { VM_MAYEXEC, "MAYEXEC" }, + { VM_MAYSHARE, "MAYSHARE" }, + { VM_GROWSDOWN, "GROWSDOWN" }, + { VM_GROWSUP, "GROWSUP" }, + { VM_SHM, "SHM" }, + { VM_DENYWRITE, "DENYWRITE" }, + { VM_EXECUTABLE, "EXECUTABLE" }, + { VM_LOCKED, "LOCKED" }, + { VM_IO , "IO " }, + { 0, "" } +}; + +int +kdbm_vm(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + struct vm_area_struct vp; + unsigned char *bp = (unsigned char *)&vp; + unsigned long addr; + long offset=0; + int nextarg; + int diag; + struct __vmflags *tp; + int i; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; imask; tp++) { + if (vp.vm_flags & tp->mask) { + kdb_printf("%s ", tp->name); + } + } + kdb_printf("\n"); + + return 0; +} + +int +kdbm_ml(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + struct module *mp; + struct module *module_list; + struct module m; + unsigned char *mcp = (unsigned char *)&m; + unsigned long addr; + long offset = 0; + int nextarg; + int diag; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + module_list = (struct module *)addr; + + kdb_printf("Module name Base Size Nsyms\n"); + for(mp = module_list; mp; mp = m.next) { + int i; + addr = (unsigned long)mp; + for(i=0; iname, argv[2]) == 0) { + for(i=0,s=mp->syms; i < mp->nsyms; i++,s++) { + kdb_printf("%s 0x%x\n", + s->name, s->value); + } + } + } + +#endif + return 0; +} + +int +kdbm_fp(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + struct file f; + unsigned char *fp = (unsigned char *)&f; + struct inode i; + unsigned char *ip = (unsigned char *)&i; + struct dentry d; + unsigned char *dp = (unsigned char *)&d; +#if 0 + unsigned char namebuf[80]; + unsigned char *np = namebuf; +#endif + int nextarg; + unsigned long addr; + unsigned long filpaddr; + long offset; + int diag; + int j; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + filpaddr = addr; + for (j=0; j\n", + d.d_name.len, d.d_name.name); + + kdb_printf(" d_count = %d d_flags = 0x%x d_inode = 0x%p\n", + d.d_count, d.d_flags, d.d_inode); + +#ifdef FIXME_for_2_4_0_test1 + kdb_printf(" d_parent = 0x%p d_mounts = 0x%p d_covers = 0x%p\n", + d.d_parent, d.d_mounts, d.d_covers); +#endif + + kdb_printf(" d_hash.nxt = 0x%p d_hash.prv = 0x%p\n", + d.d_hash.next, d.d_hash.prev); + + kdb_printf(" d_lru.nxt = 0x%p d_lru.prv = 0x%p\n", + d.d_lru.next, d.d_lru.prev); + + kdb_printf(" d_child.nxt = 0x%p d_child.prv = 0x%p\n", + d.d_child.next, d.d_child.prev); + + kdb_printf(" d_subdirs.nxt = 0x%p d_subdirs.prv = 0x%p\n", + d.d_subdirs.next, d.d_subdirs.prev); + + kdb_printf(" d_alias.nxt = 0x%p d_alias.prv = 0x%p\n", + d.d_alias.next, d.d_alias.prev); + + kdb_printf(" d_op = 0x%p d_sb = 0x%p\n\n", + d.d_op, d.d_sb); + + + kdb_printf("\nInode Entry at 0x%p\n", d.d_inode); + + kdb_printf(" i_mode = 0x%x i_nlink = %d i_rdev = 0x%x\n", + i.i_mode, i.i_nlink, i.i_rdev); + + kdb_printf(" i_ino = %ld i_count = %d i_dev = 0x%x\n", i.i_ino, + atomic_read(&i.i_count), i.i_dev); + + kdb_printf(" i_hash.nxt = 0x%p i_hash.prv = 0x%p\n", + i.i_hash.next, i.i_hash.prev); + + kdb_printf(" i_list.nxt = 0x%p i_list.prv = 0x%p\n", + i.i_list.next, i.i_list.prev); + + kdb_printf(" i_dentry.nxt = 0x%p i_dentry.prv = 0x%p\n", + i.i_dentry.next, i.i_dentry.prev); + + return 0; +} + +int +kdbm_dentry(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + struct dentry d; + unsigned char *dp = (unsigned char *)&d; +#if 0 + unsigned char namebuf[80]; + unsigned char *np = namebuf; +#endif + int nextarg; + unsigned long addr; + unsigned long dentryaddr; + long offset; + int diag; + int j; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + dentryaddr = addr; + + for (j=0; j\n", + d.d_name.len, d.d_name.name); +#if 0 + kdb_printf(" d_name: %s\n", namebuf); +#endif + + kdb_printf(" d_count = %d d_flags = 0x%x d_inode = 0x%p\n", + d.d_count, d.d_flags, d.d_inode); + +#ifdef FIXME_for_2_4_0_test1 + kdb_printf(" d_parent = 0x%p d_mounts = 0x%p d_covers = 0x%p\n", + d.d_parent, d.d_mounts, d.d_covers); +#endif + + kdb_printf(" d_hash.nxt = 0x%p d_hash.prv = 0x%p\n", + d.d_hash.next, d.d_hash.prev); + + kdb_printf(" d_lru.nxt = 0x%p d_lru.prv = 0x%p\n", + d.d_lru.next, d.d_lru.prev); + + kdb_printf(" d_child.nxt = 0x%p d_child.prv = 0x%p\n", + d.d_child.next, d.d_child.prev); + + kdb_printf(" d_subdirs.nxt = 0x%p d_subdirs.prv = 0x%p\n", + d.d_subdirs.next, d.d_subdirs.prev); + + kdb_printf(" d_alias.nxt = 0x%p d_alias.prv = 0x%p\n", + d.d_alias.next, d.d_alias.prev); + + kdb_printf(" d_op = 0x%p d_sb = 0x%p\n\n", + d.d_op, d.d_sb); + + return 0; +} + +int +kdbm_sh(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + int diag; + int nextarg; + unsigned long addr; + long offset =0L; + struct Scsi_Host sh; + unsigned char *shp = (unsigned char *)&sh; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; i < sizeof(struct Scsi_Host); i++) + *shp++ = kdba_getword(addr+i, 1); + + kdb_printf("Scsi_Host at 0x%lx\n", addr); + kdb_printf("next = 0x%p host_queue = 0x%p\n", + sh.next, sh.host_queue); + kdb_printf("ehandler = 0x%p eh_wait = 0x%p en_notify = 0x%p eh_action = 0x%p\n", + sh.ehandler, sh.eh_wait, sh.eh_notify, sh.eh_action); + kdb_printf("eh_active = 0x%d host_wait = 0x%p hostt = 0x%p host_busy = %d\n", + sh.eh_active, &sh.host_wait, sh.hostt, sh.host_active.counter); + kdb_printf("host_failed = %d extra_bytes = %d host_no = %d resetting = %d\n", + sh.host_failed, sh.extra_bytes, sh.host_no, sh.resetting); + kdb_printf("max id/lun/channel = [%d/%d/%d] this_id = %d\n", + sh.max_id, sh.max_lun, sh.max_channel, sh.this_id); + kdb_printf("can_queue = %d cmd_per_lun = %d sg_tablesize = %d u_isa_dma = %d\n", + sh.can_queue, sh.cmd_per_lun, sh.sg_tablesize, sh.unchecked_isa_dma); + kdb_printf("host_blocked = %d reverse_ordering = %d \n", + sh.host_blocked, sh.reverse_ordering); + + return 0; +} + +int +kdbm_sd(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + int diag; + int nextarg; + unsigned long addr; + long offset =0L; + struct scsi_device sd; + unsigned char *sdp = (unsigned char *)&sd; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; i < sizeof(struct scsi_device); i++) + *sdp++ = kdba_getword(addr+i, 1); + + kdb_printf("scsi_device at 0x%lx\n", addr); + kdb_printf("next = 0x%p prev = 0x%p host = 0x%p\n", + sd.next, sd.prev, sd.host); + kdb_printf("device_busy = %d device_queue 0x%p\n", + sd.device_busy, sd.device_queue); + kdb_printf("id/lun/chan = [%d/%d/%d] single_lun = %d device_blocked = %d\n", + sd.id, sd.lun, sd.channel, sd.single_lun, sd.device_blocked); + kdb_printf("queue_depth = %d current_tag = %d scsi_level = %d\n", + sd.queue_depth, sd.current_tag, sd.scsi_level); + kdb_printf("%8.8s %16.16s %4.4s\n", sd.vendor, sd.model, sd.rev); + + return 0; +} + +static char * +str_rq_status(int rq_status) +{ + switch (rq_status) { + case RQ_INACTIVE: + return "RQ_INACTIVE"; + case RQ_ACTIVE: + return "RQ_ACTIVE"; + case RQ_SCSI_BUSY: + return "RQ_SCSI_BUSY"; + case RQ_SCSI_DONE: + return "RQ_SCSI_DONE"; + case RQ_SCSI_DISCONNECTING: + return "RQ_SCSI_DISCONNECTING"; + default: + return "UNKNOWN"; + } +} + +#ifdef DO_REQUEST_PRINTING +static char * +rq_cmds[] = { + "READ", + "WRITE", + "READA", + "", + "SPECIAL", + "WRITERAW" +}; +static unsigned int num_rq_cmds = sizeof(rq_cmds) / sizeof(char *); + +static void +show_request(unsigned long addr, struct request *rq) +{ + kdb_printf("struct request at 0x%lx\n", addr); + kdb_printf("cmd: %s\n", + ((rq->cmd >=0) && (rq->cmd <= num_rq_cmds))? + rq_cmds[rq->cmd]: ""); + kdb_printf("rq_status = %s rq_dev = [%d/%d] errors = %d\n", + str_rq_status(rq->rq_status), + MAJOR(rq->rq_dev), + MINOR(rq->rq_dev), + rq->errors); + kdb_printf("sector = %d nr_sectors = %d\n", + rq->sector, rq->nr_sectors); + kdb_printf("nr_segments = %d nr_hw_segments = %d current_nr_sectors = %d\n", + rq->nr_segments, rq->nr_hw_segments, rq->current_nr_sectors); + kdb_printf("special = 0x%lx buffer = 0x%lx semaphore = 0x%lx\n", + rq->special, rq->buffer, rq->sem); + kdb_printf("bh = 0x%lx bhtail = 0x%lx next = 0x%lx\n", + rq->bh, rq->bhtail, rq->next); +} +#endif /* DO_REQUEST_PRINTING */ + +int +kdbm_sc(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + int diag; + int nextarg; + unsigned long addr; + long offset =0L; + struct scsi_cmnd sc; + unsigned char *scp = (unsigned char *)≻ + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; i < sizeof(struct scsi_cmnd); i++) + *scp++ = kdba_getword(addr+i, 1); + + kdb_printf("scsi_cmnd at 0x%lx\n", addr); + kdb_printf("host = 0x%p state = %d owner = %d device = 0x%p\nb", + sc.host, sc.state, sc.owner, sc.device); + kdb_printf("next = 0x%p reset_chain = 0x%p eh_state = %d done = 0x%p\n", + sc.next, sc.reset_chain, sc.eh_state, sc.done); + kdb_printf("serial_number = %ld serial_num_at_to = %ld retries = %d timeout = %d\n", + sc.serial_number, sc.serial_number_at_timeout, sc.retries, sc.timeout); + kdb_printf("id/lun/cmnd = [%d/%d/%d] cmd_len = %d old_cmd_len = %d\n", + sc.target, sc.lun, sc.channel, sc.cmd_len, sc.old_cmd_len); + kdb_printf("cmnd = [%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x]\n", + sc.cmnd[0], sc.cmnd[1], sc.cmnd[2], sc.cmnd[3], sc.cmnd[4], + sc.cmnd[5], sc.cmnd[6], sc.cmnd[7], sc.cmnd[8], sc.cmnd[9], + sc.cmnd[10], sc.cmnd[11]); + kdb_printf("data_cmnd = [%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x]\n", + sc.data_cmnd[0], sc.data_cmnd[1], sc.data_cmnd[2], sc.data_cmnd[3], sc.data_cmnd[4], + sc.data_cmnd[5], sc.data_cmnd[6], sc.data_cmnd[7], sc.data_cmnd[8], sc.data_cmnd[9], + sc.data_cmnd[10], sc.data_cmnd[11]); + kdb_printf("request_buffer = 0x%p bh_next = 0x%p request_bufflen = %d\n", + sc.request_buffer, sc.bh_next, sc.request_bufflen); + kdb_printf("use_sg = %d old_use_sg = %d sglist_len = %d abore_reason = %d\n", + sc.use_sg, sc.old_use_sg, sc.sglist_len, sc.abort_reason); + kdb_printf("bufflen = %d buffer = 0x%p underflow = %d transfersize = %d\n", + sc.bufflen, sc.buffer, sc.underflow, sc.transfersize); + kdb_printf("tag = %d pid = %ld\n", + sc.tag, sc.pid); + kdb_printf("request struct\n"); + kdb_printf("rq_status = %s rq_dev = [%d/%d] errors = %d cmd = %d\n", + str_rq_status(sc.request.rq_status), + MAJOR(sc.request.rq_dev), + MINOR(sc.request.rq_dev), sc.request.cmd, + sc.request.errors); + kdb_printf("sector = %ld nr_sectors = %ld current_nr_sectors = %ld\n", + sc.request.sector, sc.request.nr_sectors, sc.request.current_nr_sectors); + kdb_printf("buffer = 0x%p sem = 0x%p bh = 0x%p bhtail = 0x%p\n", + sc.request.buffer, sc.request.sem, sc.request.bh, sc.request.bhtail); +#ifdef DO_REQUEST_PRINTING + show_request(0, &sc.request); +#endif /* DO_REQUEST_PRINTING */ + + return 0; +} + +#ifdef DO_REQUEST_PRINTING +int +kdbm_req(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + int diag; + int nextarg; + unsigned long addr; + long offset =0L; + struct request req; + unsigned char *rqp = (unsigned char *)&req; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; i < sizeof(struct request); i++) + *rqp++ = kdba_getword(addr+i, 1); + + show_request(addr, &req); + + return 0; +} + +int +kdbm_rq(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + int diag; + int nextarg; + unsigned long addr; + long offset =0L; + request_queue_t rq; + struct request req; + unsigned char *rqp = (unsigned char *)&rq; + kdb_symtab_t symtab1, symtab2; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + for (i=0; i < sizeof(request_queue_t); i++) + *rqp++ = kdba_getword(addr+i, 1); + kdb_printf("request queue @ 0x%lx\n", addr); + kdbnearsym((unsigned long)rq.request_fn, &symtab1), + kdbnearsym((unsigned long)rq.merge_fn), &symtab2); + kdb_printf("current_request = 0x%p request_fn = '%s'\n merge_fn = '%s'", + rq.current_request, + symtab1.sym_name, + symtab2.sym_name); + kdbnearsym((unsigned long)rq.merge_requests_fn, &symtab1), + kdb_printf("merge_requests_fn = '%s'\n queuedata= 0x%p plugged = %d\n", + symtab1.sym_name, + rq.queuedata, rq.plugged); + kdb_printf("head_active = %d use_plug = %d\n", + rq.head_active, rq.use_plug); + kdb_printf("queue:\n"); + addr = (unsigned long)rq.current_request; + while (addr) { + rqp = (unsigned char *)&req; + for (i=0; i < sizeof(struct request); i++) + *rqp++ = kdba_getword(addr+i, 1); + show_request(addr, &req); + addr = req.next; + } + + return 0; +} +#endif /* DO_REQUEST_PRINTING */ + +int +init_module(void) +{ + kdb_register("vm", kdbm_vm, "", "Display vm_area_struct", 0); + kdb_register("modlist", kdbm_ml, "", "Display loaded modules", 0); + kdb_register("modsyms", kdbm_ms, "module-name", "Display module symbols", 0); + kdb_register("dentry", kdbm_dentry, "", "Display interesting dentry stuff", 0); + kdb_register("filp", kdbm_fp, "", "Display interesting filp stuff", 0); + kdb_register("sh", kdbm_sh, "", "Show scsi_host", 0); + kdb_register("sd", kdbm_sd, "", "Show scsi_device", 0); + kdb_register("sc", kdbm_sc, "", "Show scsi_cmnd", 0); +#ifdef DO_REQUEST_PRINTING + kdb_register("req", kdbm_req, "", "Show struct request", 0); + kdb_register("rq", kdbm_rq, "", "Show request queue", 0); +#endif /* DO_REQUEST_PRINTING */ + + return 0; +} + +void +cleanup_module(void) +{ + kdb_unregister("vm"); + kdb_unregister("modlist"); + kdb_unregister("modsyms"); + kdb_unregister("dentry"); + kdb_unregister("filp"); + kdb_unregister("sh"); + kdb_unregister("sd"); + kdb_unregister("sc"); +#ifdef DO_REQUEST_PRINTING + kdb_unregister("req"); + kdb_unregister("rq"); +#endif /* DO_REQUEST_PRINTING */ +} diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/kernel/Makefile linux/kernel/Makefile --- /opt/kernel/linux-2.4.0-test1-ac21/kernel/Makefile Tue Jun 20 03:24:47 2000 +++ linux/kernel/Makefile Fri Jun 16 00:14:33 2000 @@ -22,6 +22,10 @@ O_OBJS += kmod.o endif +ifeq ($(CONFIG_KALLSYMS),y) +O_OBJS += kallsyms.o +endif + ifeq ($(CONFIG_MODULES),y) OX_OBJS += ksyms.o endif diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/kernel/kallsyms.c linux/kernel/kallsyms.c --- /opt/kernel/linux-2.4.0-test1-ac21/kernel/kallsyms.c Thu Jan 1 01:00:00 1970 +++ linux/kernel/kallsyms.c Tue Jun 13 16:30:11 2000 @@ -0,0 +1,264 @@ +/* An example of using kallsyms data in a kernel debugger. + + Copyright 2000 Keith Owens April 2000 + + This file is part of the Linux modutils. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ident "$Id$" + +/* + This code uses the list of all kernel and module symbols to :- + + * Find any non-stack symbol in a kernel or module. Symbols do + not have to be exported for debugging. + + * Convert an address to the module (or kernel) that owns it, the + section it is in and the nearest symbol. This finds all non-stack + symbols, not just exported ones. + + You need modutils >= 2.3.11 and a kernel with the kallsyms patch + which was compiled with CONFIG_KALLSYMS. + */ + +#include +#include +#include +#include +#include + +/* These external symbols are only available on kernels compiled with + * CONFIG_KALLSYMS. + */ + +extern const char __start___kallsyms[]; +extern const char __stop___kallsyms[]; + +static struct module **kallsyms_module_list; + +static void kallsyms_get_module_list(void) +{ + const struct kallsyms_header *ka_hdr; + const struct kallsyms_section *ka_sec; + const struct kallsyms_symbol *ka_sym; + const char *ka_str; + int i; + const char *p; + + if (__start___kallsyms >= __stop___kallsyms) + return; + ka_hdr = (struct kallsyms_header *)__start___kallsyms; + ka_sec = (struct kallsyms_section *) + ((char *)(ka_hdr) + ka_hdr->section_off); + ka_sym = (struct kallsyms_symbol *) + ((char *)(ka_hdr) + ka_hdr->symbol_off); + ka_str = + ((char *)(ka_hdr) + ka_hdr->string_off); + + for (i = 0; i < ka_hdr->symbols; kallsyms_next_sym(ka_hdr, ka_sym), ++i) { + p = ka_str + ka_sym->name_off; + if (strcmp(p, "module_list") == 0) { + if (ka_sym->symbol_addr) + kallsyms_module_list = (struct module **)(ka_sym->symbol_addr); + break; + } + } +} + +static void __inline__ kallsyms_do_first_time(void) +{ + static int first_time = 1; + if (first_time) + kallsyms_get_module_list(); + first_time = 0; +} + +/* A symbol can appear in more than one module. A token is used to + * restart the scan at the next module, set the token to 0 for the + * first scan of each symbol. + */ + +int kallsyms_symbol_to_address( + const char *name, /* Name to lookup */ + unsigned long *token, /* Which module to start at */ + const char **mod_name, /* Set to module name */ + unsigned long *mod_start, /* Set to start address of module */ + unsigned long *mod_end, /* Set to end address of module */ + const char **sec_name, /* Set to section name */ + unsigned long *sec_start, /* Set to start address of section */ + unsigned long *sec_end, /* Set to end address of section */ + const char **sym_name, /* Set to full symbol name */ + unsigned long *sym_start, /* Set to start address of symbol */ + unsigned long *sym_end /* Set to end address of symbol */ + ) +{ + const struct kallsyms_header *ka_hdr = NULL; /* stupid gcc */ + const struct kallsyms_section *ka_sec; + const struct kallsyms_symbol *ka_sym = NULL; + const char *ka_str = NULL; + const struct module *m; + int i = 0, l; + const char *p, *pt_R; + char *p2; + + kallsyms_do_first_time(); + + /* Restart? */ + m = *kallsyms_module_list; + if (token && *token) { + for (; m; m = m->next) + if ((unsigned long)m == *token) + break; + if (m) + m = m->next; + } + + for (; m; m = m->next) { + if (!mod_member_present(m, kallsyms_start) || + !mod_member_present(m, kallsyms_end) || + m->kallsyms_start >= m->kallsyms_end) + continue; + ka_hdr = (struct kallsyms_header *)m->kallsyms_start; + ka_sym = (struct kallsyms_symbol *) + ((char *)(ka_hdr) + ka_hdr->symbol_off); + ka_str = + ((char *)(ka_hdr) + ka_hdr->string_off); + for (i = 0; i < ka_hdr->symbols; ++i, kallsyms_next_sym(ka_hdr, ka_sym)) { + p = ka_str + ka_sym->name_off; + if (strcmp(p, name) == 0) + break; + /* Unversioned requests match versioned names */ + if (!(pt_R = strstr(p, "_R"))) + continue; + l = strlen(pt_R); + if (l < 10) + continue; /* Not _R.*xxxxxxxx */ + (void)simple_strtoul(pt_R+l-8, &p2, 16); + if (*p2) + continue; /* Not _R.*xxxxxxxx */ + if (strncmp(p, name, pt_R-p) == 0) + break; /* Match with version */ + } + if (i < ka_hdr->symbols) + break; + } + + if (token) + *token = (unsigned long)m; + if (!m) + return(0); /* not found */ + + ka_sec = (const struct kallsyms_section *) + ((char *)ka_hdr + ka_hdr->section_off + ka_sym->section_off); + *mod_name = *(m->name) ? m->name : "kernel"; + *mod_start = ka_hdr->start; + *mod_end = ka_hdr->end; + *sec_name = ka_sec->name_off + ka_str; + *sec_start = ka_sec->start; + *sec_end = ka_sec->start + ka_sec->size; + *sym_name = ka_sym->name_off + ka_str; + *sym_start = ka_sym->symbol_addr; + if (i < ka_hdr->symbols-1) { + const struct kallsyms_symbol *ka_symn = ka_sym; + kallsyms_next_sym(ka_hdr, ka_symn); + *sym_end = ka_symn->symbol_addr; + } + else + *sym_end = *sec_end; + return(1); +} + +int kallsyms_address_to_symbol( + unsigned long address, /* Address to lookup */ + const char **mod_name, /* Set to module name */ + unsigned long *mod_start, /* Set to start address of module */ + unsigned long *mod_end, /* Set to end address of module */ + const char **sec_name, /* Set to section name */ + unsigned long *sec_start, /* Set to start address of section */ + unsigned long *sec_end, /* Set to end address of section */ + const char **sym_name, /* Set to full symbol name */ + unsigned long *sym_start, /* Set to start address of symbol */ + unsigned long *sym_end /* Set to end address of symbol */ + ) +{ + const struct kallsyms_header *ka_hdr = NULL; /* stupid gcc */ + const struct kallsyms_section *ka_sec = NULL; + const struct kallsyms_symbol *ka_sym; + const char *ka_str; + const struct module *m; + int i; + unsigned long end; + + kallsyms_do_first_time(); + + for (m = *kallsyms_module_list; m; m = m->next) { + if (!mod_member_present(m, kallsyms_start) || + !mod_member_present(m, kallsyms_end) || + m->kallsyms_start >= m->kallsyms_end) + continue; + ka_hdr = (struct kallsyms_header *)m->kallsyms_start; + ka_sec = (const struct kallsyms_section *) + ((char *)ka_hdr + ka_hdr->section_off); + /* Is the address in any section in this module? */ + for (i = 0; i < ka_hdr->sections; ++i, kallsyms_next_sec(ka_hdr, ka_sec)) { + if (ka_sec->start <= address && + (ka_sec->start + ka_sec->size) > address) + break; + } + if (i < ka_hdr->sections) + break; /* Found a matching section */ + } + + if (!m) + return(0); /* not found */ + + ka_sym = (struct kallsyms_symbol *) + ((char *)(ka_hdr) + ka_hdr->symbol_off); + ka_str = + ((char *)(ka_hdr) + ka_hdr->string_off); + *mod_name = *(m->name) ? m->name : "kernel"; + *mod_start = ka_hdr->start; + *mod_end = ka_hdr->end; + *sec_name = ka_sec->name_off + ka_str; + *sec_start = ka_sec->start; + *sec_end = ka_sec->start + ka_sec->size; + *sym_name = *sec_name; /* In case we find no matching symbol */ + *sym_start = *sec_start; + *sym_end = *sec_end; + + for (i = 0; i < ka_hdr->symbols; ++i, kallsyms_next_sym(ka_hdr, ka_sym)) { + if (ka_sym->symbol_addr > address) + continue; + if (i < ka_hdr->symbols-1) { + const struct kallsyms_symbol *ka_symn = ka_sym; + kallsyms_next_sym(ka_hdr, ka_symn); + end = ka_symn->symbol_addr; + } + else + end = *sec_end; + if (end <= address) + continue; + if ((char *)ka_hdr + ka_hdr->section_off + ka_sym->section_off + != (char *)ka_sec) + continue; /* wrong section */ + *sym_name = ka_str + ka_sym->name_off; + *sym_start = ka_sym->symbol_addr; + *sym_end = end; + break; + } + return(1); +} diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/kernel/ksyms.c linux/kernel/ksyms.c --- /opt/kernel/linux-2.4.0-test1-ac21/kernel/ksyms.c Tue Jun 20 03:24:47 2000 +++ linux/kernel/ksyms.c Sat Jun 17 23:43:54 2000 @@ -52,6 +52,9 @@ #ifdef CONFIG_KMOD #include #endif +#ifdef CONFIG_KALLSYMS +#include +#endif extern int console_loglevel; extern void set_device_ro(kdev_t dev,int flag); @@ -85,6 +88,15 @@ EXPORT_SYMBOL(try_inc_mod_count); #endif +#ifdef CONFIG_KALLSYMS +extern const char __start___kallsyms[]; +extern const char __stop___kallsyms[]; +EXPORT_SYMBOL(__start___kallsyms); +EXPORT_SYMBOL(__stop___kallsyms); +EXPORT_SYMBOL(kallsyms_symbol_to_address); +EXPORT_SYMBOL(kallsyms_address_to_symbol); +#endif + /* process memory management */ EXPORT_SYMBOL(do_mmap_pgoff); EXPORT_SYMBOL(do_munmap); @@ -184,6 +196,7 @@ EXPORT_SYMBOL(set_blocksize); EXPORT_SYMBOL(getblk); EXPORT_SYMBOL(bdget); +EXPORT_SYMBOL(show_buffers); EXPORT_SYMBOL(bdput); EXPORT_SYMBOL(bread); EXPORT_SYMBOL(breada); @@ -248,6 +261,10 @@ EXPORT_SYMBOL(block_symlink); EXPORT_SYMBOL(vfs_readdir); EXPORT_SYMBOL(dcache_readdir); +EXPORT_SYMBOL(elevator_init); + +extern void end_buffer_io_async(struct buffer_head *bh, int uptodate); +EXPORT_SYMBOL(end_buffer_io_async); /* for stackable file systems (lofs, wrapfs, cryptfs, etc.) */ EXPORT_SYMBOL(default_llseek); @@ -273,7 +290,6 @@ /* block device driver support */ EXPORT_SYMBOL(block_read); EXPORT_SYMBOL(block_write); -EXPORT_SYMBOL(wait_for_request); EXPORT_SYMBOL(blksize_size); EXPORT_SYMBOL(hardsect_size); EXPORT_SYMBOL(blk_size); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/kernel/module.c linux/kernel/module.c --- /opt/kernel/linux-2.4.0-test1-ac21/kernel/module.c Tue Jun 20 03:24:47 2000 +++ linux/kernel/module.c Tue Jun 13 16:30:11 2000 @@ -14,11 +14,12 @@ * Heavily modified by Bjorn Ekwall May 1994 (C) * Rewritten by Richard Henderson Dec 1996 * Add MOD_INITIALIZING Keith Owens Nov 1999 + * Add kallsyms support, Keith Owens April 2000 * * This source is covered by the GNU GPL, the same as all kernel sources. */ -#ifdef CONFIG_MODULES /* a *big* #ifdef block... */ +#if defined(CONFIG_MODULES) || defined(CONFIG_KALLSYMS) extern struct module_symbol __start___ksymtab[]; extern struct module_symbol __stop___ksymtab[]; @@ -26,6 +27,9 @@ extern const struct exception_table_entry __start___ex_table[]; extern const struct exception_table_entry __stop___ex_table[]; +extern const char __start___kallsyms[]; +extern const char __stop___kallsyms[]; + static struct module kernel_module = { sizeof(struct module), /* size_of_struct */ @@ -43,10 +47,16 @@ NULL, /* cleanup */ __start___ex_table, /* ex_table_start */ __stop___ex_table, /* ex_table_end */ + kallsyms_start: __start___kallsyms, + kallsyms_end: __stop___kallsyms, /* Rest are NULL */ }; struct module *module_list = &kernel_module; + +#endif /* defined(CONFIG_MODULES) || defined(CONFIG_KALLSYMS) */ + +#if defined(CONFIG_MODULES) /* The rest of the source */ static long get_mod_name(const char *user_name, char **buf); static void put_mod_name(char *buf); diff -urN --exclude-from /home/axboe/exclude /opt/kernel/linux-2.4.0-test1-ac21/mm/vmalloc.c linux/mm/vmalloc.c --- /opt/kernel/linux-2.4.0-test1-ac21/mm/vmalloc.c Mon May 22 05:18:08 2000 +++ linux/mm/vmalloc.c Tue Jun 13 16:30:11 2000 @@ -16,6 +16,43 @@ rwlock_t vmlist_lock = RW_LOCK_UNLOCKED; 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;