Index: 2.2.15pre19/Documentation/Configure.help --- 2.2.15pre19/Documentation/Configure.help Thu, 20 Apr 2000 22:09:39 +1000 kaos (linux-2.2/I/b/39_Configure. 1.2.1.3.1.7.6.1.2.1 644) +++ 2.2.15pre19/Documentation/Configure.help Fri, 21 Apr 2000 18:04:41 +1000 kaos (linux-2.2/I/b/39_Configure. 1.2.1.3.1.7.6.1.2.3 644) @@ -10220,6 +10220,30 @@ 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. Selecting + this option will automatically set CONFIG_KALLSYMS. + +Kernel Debugging backtrace framepointer support +CONFIG_KDB_FRAMEPTR + This option causes the system to use the frame pointer as a + real frame pointer rather than as a general purpose register. Set + this option to get accurate stack tracebacks when using the + built-in kernel debugger. + +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 ISDN ("Integrated Services Digital Networks", called RNIS in France) Index: 2.2.15pre19/Documentation/kdb/kdb.mm --- 2.2.15pre19/Documentation/kdb/kdb.mm Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/Documentation/kdb/kdb.mm Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/t/c/8_kdb.mm 1.1 644) @@ -0,0 +1,97 @@ +.ND "March 10, 1999" +.TL +Built-in Kernel Debugger for Linux +.AU "Scott Lurndal" SL 8U500 OS 33158 +.AT "Member Technical Staff" +.AF "Silicon Graphics, Inc." +.MT 2 +.AS +These programmer notes describe the built-in kernel debugger +for linux. +.AE +.H 1 "Overview" +This document describes the built-in kernel debugger available +for linux. This debugger allows the programmer to interactivly +examine kernel memory, disassemble kernel functions, set breakpoints +in the kernel code and display and modify register contents. +.P +A symbol table is included in the kernel image which enables all +symbols with global scope (including static symbols) to be used +as arguments to the kernel debugger commands. +.H 1 "Getting Started" +To include the kernel debugger in a linux kernel, use a +configuration mechanism (e.g. xconfig, menuconfig, et. al.) +to enable the \fBCONFIG_KDB\fP option. Additionally, for accurate +stack tracebacks, it is recommended that the \fBCONFIG_KDB_FRAMEPTR\fP +option be enabled. \fBCONFIG_KDB_FRAMEPTR\fP changes the compiler +flags so that the frame pointer register will be used as a frame +pointer rather than a general purpose register. +.P +After linux has been configured to include the kernel debugger, +make a new kernel with the new configuration file (a make clean +is recommended before making the kernel), and install the kernel +as normal. +.P +When booting the new kernel using \fIlilo\fP(1), the 'kdb' flag +may be added after the image name on the \fBLILO\fP boot line to +force the kernel to stop in the kernel debugger early in the +kernel initialization process. If the kdb flag isn't provided, +then kdb will automatically be invoked upon system panic or +when the +\fBPAUSE\fP +key is used from the keyboard. +.P +Kdb can also be used via the serial port. Set up the system to +have a serial console (see \fIDocumentation/serial-console.txt\fP). +The \fBControl-A\fP key sequence on the serial port will cause the +kernel debugger to be entered with input from the serial port and +output to the serial console. +.H 2 "Basic Commands" +There are several categories of commands available to the +kernel debugger user including commands providing memory +display and modification, register display and modification, +instruction disassemble, breakpoints and stack tracebacks. +.P +The following table shows the currently implemented commands: +.DS +.TS +box, center; +l | l +l | l. +Command Description +_ +bc Clear Breakpoint +bd Disable Breakpoint +be Enable Breakpoint +bl Display breakpoints +bp Set or Display breakpoint +bpa Set or Display breakpoint globally +cpu Switch cpus +env Show environment +go Restart execution +help Display help message +id Disassemble Instructions +ll Follow Linked Lists +md Display memory contents +mds Display memory contents symbolically +mm Modify memory contents +reboot Reboot the machine +rd Display register contents +rm Modify register contents +set Add/change environment variable +.TE +.DE +.P +Further information on the above commands can be found in +the appropriate manual pages. Some commands can be abbreviated, such +commands are indicated by a non-zero \fIminlen\fP parameter to +\fBkdb_register\fP; the value of \fIminlen\fP being the minimum length +to which the command can be abbreviated (for example, the \fBgo\fP +command can be abbreviated legally to \fBg\fP). +.P +If an input string does not match a command in the command table, +it is treated as an address expression and the corresponding address +value and nearest symbol are shown. +.H 1 Writing new commands +.H 2 Writing a built-in command +.H 2 Writing a modular command Index: 2.2.15pre19/Documentation/kdb/kdb_bp.man --- 2.2.15pre19/Documentation/kdb/kdb_bp.man Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/Documentation/kdb/kdb_bp.man Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/t/c/7_kdb_bp.man 1.1 644) @@ -0,0 +1,131 @@ +.TH BD 1 "09 March 1999" +.SH NAME +bc, bd, be, bl, bp, bpa \- breakpoint commands +.SH SYNOPSIS +bp \fIaddress-expression\fP [\f(CWDATAR|DATAW|IO\fP [\fIlength\fP]] +.LP +bpa \fIaddress-expression\fP [\f(CWDATAR|DATAW|IO\fP [\fIlength\fP]] +.LP +bd \fIbreakpoint-number\fP +.LP +bc \fIbreakpoint-number\fP +.LP +be \fIbreakpoint-number\fP +.LP +bl +.SH DESCRIPTION +The +.B bp +command is used to establish a breakpoint. +The \fIaddress-expression\fP may be a numeric value (decimal or +hexidecimal), a symbol name, a register name preceeded by a +percent symbol '%', or a simple expression consisting of a +symbol name, an addition or subtraction character and a numeric +value (decimal or hexidecimal). +.P +The \fIaddress-expression\fP may also consist of a single +asterisk '*' symbol which indicates that the command should +operate on all existing breakpoints (valid only for \fBbc\fP, +\fBbd\fP and \fBbe\fP). +.P +Three different types of +breakpoints may be set: + +.TP 8 +Instruction +Causes the kernel debugger to be invoked from the debug exception +path when an instruction is fetched from the specified address. This +is the default if no other type of breakpoint is requested. + +.TP 8 +DATAR +Causes the kernel debugger to be entered when data of length +\fIlength\fP is read from or written to the specified address. +This type of breakpoint must use a processor debug register +thus placing a limit of four on the number of data and I/O +breakpoints that may be established. + +.TP 8 +DATAW +Enters the kernel debugger when data of length \fIlength\fP +is written to the specified address. \fIlength\fP defaults +to four bytes if it is not explicitly specified. Note that the +processor will have already overwritten the prior data at the +breakpoint location before the kernel debugger is invoked. The +prior data should be saved before establishing the +breakpoint, if required. + +.TP 8 +IO +Enters the kernel debugger when an \fBin\fP or \fBout\fP instruction +targets the specified I/O address. + +.P +The +.B bpa +command will establish a breakpoint on all processors in an +SMP system. This command is not available in an uniprocessor +kernel. +.P +The +.B bd +command will disable a breakpoint without removing it from +the kernel debuggers breakpoint table. This can be used to +keep more than 4 breakpoints in the breakpoint table without +exceeding the processor breakpoint register count. +.P +The +.B be +command will re-enable a disabled breakpoint. +.P +The +.B bc +command will clear a breakpoint from the breakpoint table. +.P +The +.B bl +command will list the existing set of breakpoints. +.SH LIMITATIONS +Currently the kernel debugger does not use the int 03 method +of establishing instruction breakpoints, so there may only be +four active instruction and data breakpoints at any given time. +.P +There is a compile time limit of sixteen entries in the +breakpoint table at any one time. +.SH ENVIRONMENT +The breakpoint subsystem does not currently use any environment +variables. +.SH SMP CONSIDERATIONS +Breakpoints which use the processor breakpoint registers +are only established on the processor which is +currently active. If you wish breakpoints to be universal +use the 'bpa' command. +.SH EXAMPLES +.TP 8 +bp schedule +Sets an instruction breakpoint at the begining of the +function \fBschedule\fP. + +.TP 8 +bp schedule+0x12e +Sets an instruction breakpoint at the instruction located +at \fBschedule\fP+\fI0x12e\fP. + +.TP 8 +bp ttybuffer+0x24 dataw +Sets a data write breakpoint at the location referenced by +\fBttybuffer\fP+\fI0x24\fP for a length of four bytes. + +.TP 8 +bp 0xc0254010 datar 1 +Establishes a data reference breakpoint at address \fB0xc0254010\fP +for a length of one byte. + +.TP 8 +bp +List current breakpoint table. + +.TP 8 +bd 0 +Disable breakpoint #0. + Index: 2.2.15pre19/Documentation/kdb/kdb_bt.man --- 2.2.15pre19/Documentation/kdb/kdb_bt.man Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/Documentation/kdb/kdb_bt.man Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/t/c/6_kdb_bt.man 1.1 644) @@ -0,0 +1,67 @@ +.TH BT 1 "15 March 1999" +.SH NAME +bt \- Stack Traceback command +.SH SYNOPSIS +bt [ ] +.LP +btp +.SH DESCRIPTION +The +.B bt +command is used to print a stack traceback. It uses the +current registers (see \fBrd\fP command) to determine +the starting context and attempts to provide a complete +stack traceback for the active thread. If \fIstack-frame-address\fP +is supplied, it is assumed to point to the start of a valid +stack frame and the stack will be traced back from that +point (e.g. on i386 architecture, \fIstack-frame-address\fP +should be the stack address of a saved \fB%eip\fP value from a \fBcall\fP +instruction). +.P +A kernel configuration option \fBCONFIG_KDB_FRAMEPTR\fP should +be enabled so that the compiler will utilize the frame pointer +register properly to maintain a stack which can be correctly +analyzed. +.P +The \fBbt\fP command will attempt to analyze the stack without +frame pointers if the \fBCONFIG_KDB_FRAMEPTR\fP option is not +enabled, but the analysis is difficult and may not produce +accurate nor complete results. +.P +The \fBbtp\fP command will analyze the stack for the given +process identification (see the \fBps\fP command). +.SH LIMITATIONS +If the kernel is compiled without frame pointers, stack tracebacks +may be incomplete. The \fBmds %esp\fP command may be useful in +attemping to determine the actual stack traceback manually. +.P +The \fBbt\fP command may print more arguments for a function +than that function accepts; this happens when the C compiler +doesn't immediately pop the arguments off the stack upon return +from a called function. When this is this case, these extra +stack words will be considered additional arguments by the \fBbt\fP +command. +.SH ENVIRONMENT +The \fBBTARGS\fP environment variable governs the maximum number +of arguments that are printed for any single function. +.SH SMP CONSIDERATIONS +None. +.SH EXAMPLES +.nf +.na +.ft CW +[root@host /root]# cat /proc/partitions +Entering kdb on processor 0 due to Debug Exception @ 0xc01845e3 +Read/Write breakpoint #1 at 0xc024ddf4 +kdb> bt + EBP Caller Function(args) +0xc74f5f44 0xc0146166 get_partition_list(0xc74d8000) +0xc74f5f8c 0xc01463f3 get_root_array(0xc74d8000, 0x13, 0xc74f5f88, 0xf3, 0xc00) +0xc74f5fbc 0xc0126138 array_read(0xc76cd80, 0x804aef8, 0xc00, 0xc76cdf94) +0xbffffcd4 0xc0108b30 sys_read(0x3, 0x804aef8, 0x1000, 0x1000, 0x804aef8) +kdb> bp +Instruction Breakpoint #0 at 0xc0111ab8 (schedule) in dr0 is disabled on cpu 0 +Data Access Breakpoint #1 at 0xc024ddf4 (gendisk_head) in dr1 is enabled on cpu 0 +for 4 bytes +kdb> go +[root@host /root]# Index: 2.2.15pre19/Documentation/kdb/kdb_env.man --- 2.2.15pre19/Documentation/kdb/kdb_env.man Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/Documentation/kdb/kdb_env.man Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/t/c/5_kdb_env.ma 1.1 644) @@ -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. Index: 2.2.15pre19/Documentation/kdb/kdb_ll.man --- 2.2.15pre19/Documentation/kdb/kdb_ll.man Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/Documentation/kdb/kdb_ll.man Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/t/c/4_kdb_ll.man 1.1 644) @@ -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> Index: 2.2.15pre19/Documentation/kdb/kdb_md.man --- 2.2.15pre19/Documentation/kdb/kdb_md.man Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/Documentation/kdb/kdb_md.man Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/t/c/3_kdb_md.man 1.1 644) @@ -0,0 +1,86 @@ +.TH MD 1 "09 March 1999" +.SH NAME +md, mds, mm\- Memory manipulation commands +.SH SYNOPSIS +md [ \fIaddress-expression\fP [ \fIline-count\fP [\fIoutput-radix\fP ] ] ] +.LP +mds [ \fIaddress-expression\fP [ \fIline-count\fP [\fIoutput-radix\fP ] ] ] +.LP +mm \fIaddress-expression\fP \fInew-contents\fP +.SH DESCRIPTION +The +.B md +command is used to display the contents of memory. +The \fIaddress-expression\fP may be a numeric value (decimal or +hexidecimal), a symbol name, a register name preceeded by one or more +percent symbols '%', an environment variable name preceeded by +a currency symbol '$', or a simple expression consisting of a +symbol name, an addition or subtraction character and a numeric +value (decimal or hexidecimal). +.P +If the \fIline-count\fP or \fIradix\fP arguments are omitted, +they default to the values of the \fBMDCOUNT\fP and \fBRADIX\fP +environment variables respectively. If the \fBMDCOUNT\fP or +\fBRADIX\fP environment variables are unset, the appropriate +defaults will be used [see \fBENVIRONMENT\fP below]. +.P +The +.B mds +command displays the contents of memory one word per line and +attempts to correlate the contents of each word with a symbol +in the symbol table. If no symbol is found, the ascii representation +of the word is printed, otherwise the symbol name and offset from +symbol value are printed. +.P +The +.B mm +command allows modification of memory. The word at the address +represented by \fIaddress-expression\fP is changed to +\fInew-contents\fP. \fInew-contents\fP is allowed to be an +\fIaddress-expression\fP. +.SH LIMITATIONS +None. +.SH ENVIRONMENT +.TP 8 +MDCOUNT +This environment variable (default=8) defines the number of lines +that will be displayed by each invocation of the \fBmd\fP command. + +.TP 8 +RADIX +This environment variable (default=16) defines the radix used to +print the memory contents. + +.TP 8 +BYTESPERWORD +This environment variable (default=4) selects the width of output +data when printing memory contents. Select the value two to get +16-bit word output, select the value one to get byte output. + +.TP 8 +LINES +This environment variable governs the number of lines of output +that will be presented before the kernel debugger built-in pager +pauses the output. This variable only affects the functioning +of the \fBmd\fP and \fBmds\fP if the \fBMDCOUNT\fP variable +is set to a value greater than the \fBLINES\fP variable. +.SH SMP CONSIDERATIONS +None. +.SH EXAMPLES +.TP 8 +md %edx +Display memory starting at the address contained in register \fB%edx\fP. + +.TP 8 +mds %esp +Display stack contents symbolically. This command is quite useful +in manual stack traceback. + +.TP 8 +mm 0xc0252110 0x25 +Change the memory location at 0xc0252110 to the value 0x25. + +.TP 8 +md chrdev_table 15 +Display 15 lines (at 16 bytes per line) starting at address +represented by the symbol \fIchrdev_table\fP. Index: 2.2.15pre19/Documentation/kdb/kdb_rd.man --- 2.2.15pre19/Documentation/kdb/kdb_rd.man Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/Documentation/kdb/kdb_rd.man Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/t/c/2_kdb_rd.man 1.1 644) @@ -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. Index: 2.2.15pre19/Documentation/kdb/kdb_ss.man --- 2.2.15pre19/Documentation/kdb/kdb_ss.man Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/Documentation/kdb/kdb_ss.man Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/t/c/1_kdb_ss.man 1.1 644) @@ -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> + Index: 2.2.15pre19/Makefile --- 2.2.15pre19/Makefile Thu, 20 Apr 2000 22:09:39 +1000 kaos (linux-2.2/G/b/14_Makefile 1.3.2.2.1.1.1.5.1.3.6.1.2.1 644) +++ 2.2.15pre19/Makefile Fri, 21 Apr 2000 18:04:41 +1000 kaos (linux-2.2/G/b/14_Makefile 1.3.2.2.1.1.1.5.1.3.6.1.2.3 644) @@ -31,6 +31,7 @@ OBJDUMP =$(CROSS_COMPILE)objdump MAKE =make GENKSYMS=/sbin/genksyms +KALLSYMS=/sbin/kallsyms all: do-it-all @@ -87,7 +88,11 @@ # standard CFLAGS # +ifdef CONFIG_KDB_FRAMEPTR +CFLAGS = -Wall -Wstrict-prototypes -O2 +else CFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer +endif # use '-fno-strict-aliasing', but only if the compiler can take it CFLAGS += $(shell if $(CC) -fno-strict-aliasing -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-fno-strict-aliasing"; fi) @@ -223,17 +228,36 @@ boot: vmlinux @$(MAKE) -C arch/$(ARCH)/boot +LD_VMLINUX := $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \ + --start-group \ + $(CORE_FILES) \ + $(FILESYSTEMS) \ + $(NETWORKS) \ + $(DRIVERS) \ + $(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) \ - $(FILESYSTEMS) \ - $(NETWORKS) \ - $(DRIVERS) \ - $(LIBS) \ - --end-group \ - -o vmlinux + @if [ "$(CONFIG_KALLSYMS)" = "y" ]; then \ + echo kallsyms pass 1 && \ + echo $(LD_VMLINUX) -o _tmp_vmlinux1 && \ + $(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; \ + fi + $(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 Index: 2.2.15pre19/arch/alpha/vmlinux.lds --- 2.2.15pre19/arch/alpha/vmlinux.lds Fri, 05 Feb 1999 09:47:48 +1100 keith (linux-2.2/E/b/15_vmlinux.ld 1.1 644) +++ 2.2.15pre19/arch/alpha/vmlinux.lds Thu, 20 Apr 2000 22:49:11 +1000 kaos (linux-2.2/E/b/15_vmlinux.ld 1.2 644) @@ -21,6 +21,10 @@ __stop___ksymtab = .; .kstrtab : { *(.kstrtab) } + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + /* Startup code */ . = ALIGN(8192); __init_begin = .; Index: 2.2.15pre19/arch/arm/vmlinux-armo.lds --- 2.2.15pre19/arch/arm/vmlinux-armo.lds Fri, 05 Feb 1999 09:47:48 +1100 keith (linux-2.2/p/b/6_vmlinux-ar 1.1 644) +++ 2.2.15pre19/arch/arm/vmlinux-armo.lds Thu, 20 Apr 2000 22:49:11 +1000 kaos (linux-2.2/p/b/6_vmlinux-ar 1.2 644) @@ -26,6 +26,10 @@ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + _etext = .; /* End of text section */ . = ALIGN(8192); Index: 2.2.15pre19/arch/arm/vmlinux-armv.lds --- 2.2.15pre19/arch/arm/vmlinux-armv.lds Fri, 05 Feb 1999 09:47:48 +1100 keith (linux-2.2/p/b/4_vmlinux-ar 1.1 644) +++ 2.2.15pre19/arch/arm/vmlinux-armv.lds Thu, 20 Apr 2000 22:49:11 +1000 kaos (linux-2.2/p/b/4_vmlinux-ar 1.1.1.1 644) @@ -26,6 +26,10 @@ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + _etext = .; /* End of text section */ . = ALIGN(8192); Index: 2.2.15pre19/arch/i386/Makefile --- 2.2.15pre19/arch/i386/Makefile Thu, 20 Apr 2000 22:09:39 +1000 kaos (linux-2.2/G/b/1_Makefile 1.1.1.2.4.1 644) +++ 2.2.15pre19/arch/i386/Makefile Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/G/b/1_Makefile 1.1.1.2.4.2 644) @@ -49,6 +49,12 @@ CORE_FILES := arch/i386/kernel/kernel.o arch/i386/mm/mm.o $(CORE_FILES) LIBS := $(TOPDIR)/arch/i386/lib/lib.a $(LIBS) $(TOPDIR)/arch/i386/lib/lib.a +ifdef CONFIG_KDB +LIBS := $(LIBS) $(TOPDIR)/arch/i386/kdb/kdb.a +CORE_FILES := $(CORE_FILES) arch/i386/kdb/kdb.o +SUBDIRS := $(SUBDIRS) arch/i386/kdb +endif + ifdef CONFIG_MATH_EMULATION SUBDIRS := $(SUBDIRS) arch/i386/math-emu DRIVERS := $(DRIVERS) arch/i386/math-emu/math.a @@ -59,6 +65,11 @@ arch/i386/mm: dummy $(MAKE) linuxsubdirs SUBDIRS=arch/i386/mm + +ifdef CONFIG_KDB +arch/i386/kdb: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/i386/kdb +endif MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot Index: 2.2.15pre19/arch/i386/boot/compressed/misc.c --- 2.2.15pre19/arch/i386/boot/compressed/misc.c Wed, 05 Jan 2000 09:26:04 +1100 keith (linux-2.2/G/b/4_misc.c 1.1.4.1 644) +++ 2.2.15pre19/arch/i386/boot/compressed/misc.c Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/G/b/4_misc.c 1.1.4.2 644) @@ -104,7 +104,7 @@ #define LOW_BUFFER_START 0x2000 #define LOW_BUFFER_END 0x90000 #define LOW_BUFFER_SIZE ( LOW_BUFFER_END - LOW_BUFFER_START ) -#define HEAP_SIZE 0x2400 +#define HEAP_SIZE 0x3000 static int high_loaded =0; static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/; Index: 2.2.15pre19/arch/i386/config.in --- 2.2.15pre19/arch/i386/config.in Thu, 20 Apr 2000 22:09:39 +1000 kaos (linux-2.2/G/b/0_config.in 1.2.2.2.2.2 644) +++ 2.2.15pre19/arch/i386/config.in Fri, 21 Apr 2000 18:04:41 +1000 kaos (linux-2.2/G/b/0_config.in 1.2.2.2.2.4 644) @@ -207,5 +207,15 @@ #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 + bool 'Compile the kernel with frame pointers' CONFIG_KDB_FRAMEPTR +fi +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 endmenu Index: 2.2.15pre19/arch/i386/kdb/Makefile --- 2.2.15pre19/arch/i386/kdb/Makefile Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/arch/i386/kdb/Makefile Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/t/c/0_Makefile 1.1 644) @@ -0,0 +1,21 @@ +# +# Makefile for i386-specific kdb files.. +# +# Copyright 1999, Silicon Graphics Inc. +# +# Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o + +L_TARGET = kdb.a +L_OBJS = kdb.o i386-dis.o kd_id.o kdbsupport.o kdb_io.o kdb_bp.o kdb_bt.o + +MOD_SUB_DIRS += modules + +include $(TOPDIR)/Rules.make Index: 2.2.15pre19/arch/i386/kdb/dis-asm.h --- 2.2.15pre19/arch/i386/kdb/dis-asm.h Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/arch/i386/kdb/dis-asm.h Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/s/c/51_dis-asm.h 1.1 644) @@ -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) */ Index: 2.2.15pre19/arch/i386/kdb/i386-dis.c --- 2.2.15pre19/arch/i386/kdb/i386-dis.c Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/arch/i386/kdb/i386-dis.c Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/s/c/50_i386-dis.c 1.1 644) @@ -0,0 +1,2308 @@ +/* Print i386 instructions for GDB, the GNU debugger. + Copyright (C) 1988, 89, 91, 93, 94, 95, 96, 1997 + Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* + * 80386 instruction printer by Pace Willisson (pace@prep.ai.mit.edu) + * July 1988 + * modified by John Hassey (hassey@dg-rtp.dg.com) + */ + +/* + * The main tables describing the instructions is essentially a copy + * of the "Opcode Map" chapter (Appendix A) of the Intel 80386 + * Programmers Manual. Usually, there is a capital letter, followed + * by a small letter. The capital letter tell the addressing mode, + * and the small letter tells about the operand size. Refer to + * the Intel manual for details. + */ + +#include "dis-asm.h" +#if defined(__KERNEL__) +#include +#include +#endif + +#define MAXLEN 20 + +#if defined(STANDALONE) +#include +#endif /* STANDALONE */ + +static int fetch_data PARAMS ((struct disassemble_info *, bfd_byte *)); + +struct dis_private +{ + /* Points to first byte not fetched. */ + bfd_byte *max_fetched; + bfd_byte the_buffer[MAXLEN]; + bfd_vma insn_start; +#if defined(STANDALONE) + jmp_buf bailout; +#endif /* STANDALONE */ +}; + +/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive) + to ADDR (exclusive) are valid. Returns 1 for success, longjmps + on error. */ +#define FETCH_DATA(info, addr) \ + ((addr) <= ((struct dis_private *)(info->private_data))->max_fetched \ + ? 1 : fetch_data ((info), (addr))) + +static int +fetch_data (info, addr) + struct disassemble_info *info; + bfd_byte *addr; +{ + int status; + struct dis_private *priv = (struct dis_private *)info->private_data; + bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer); + + status = (*info->read_memory_func) (start, + priv->max_fetched, + addr - priv->max_fetched, + info); + if (status != 0) + { + (*info->memory_error_func) (status, start, info); +#if defined(STANDALONE) + longjmp (priv->bailout, 1); +#else + /* XXX - what to do? */ + printk("Hmm. longjmp.\n"); +#endif + } + else + priv->max_fetched = addr; + return 1; +} + +#define Eb OP_E, b_mode +#define indirEb OP_indirE, b_mode +#define Gb OP_G, b_mode +#define Ev OP_E, v_mode +#define indirEv OP_indirE, v_mode +#define Ew OP_E, w_mode +#define Ma OP_E, v_mode +#define M OP_E, 0 +#define Mp OP_E, 0 /* ? */ +#define Gv OP_G, v_mode +#define Gw OP_G, w_mode +#define Rw OP_rm, w_mode +#define Rd OP_rm, d_mode +#define Ib OP_I, b_mode +#define sIb OP_sI, b_mode /* sign extened byte */ +#define Iv OP_I, v_mode +#define Iw OP_I, w_mode +#define Jb OP_J, b_mode +#define Jv OP_J, v_mode +#if 0 +#define ONE OP_ONE, 0 +#endif +#define Cd OP_C, d_mode +#define Dd OP_D, d_mode +#define Td OP_T, d_mode + +#define eAX OP_REG, eAX_reg +#define eBX OP_REG, eBX_reg +#define eCX OP_REG, eCX_reg +#define eDX OP_REG, eDX_reg +#define eSP OP_REG, eSP_reg +#define eBP OP_REG, eBP_reg +#define eSI OP_REG, eSI_reg +#define eDI OP_REG, eDI_reg +#define AL OP_REG, al_reg +#define CL OP_REG, cl_reg +#define DL OP_REG, dl_reg +#define BL OP_REG, bl_reg +#define AH OP_REG, ah_reg +#define CH OP_REG, ch_reg +#define DH OP_REG, dh_reg +#define BH OP_REG, bh_reg +#define AX OP_REG, ax_reg +#define DX OP_REG, dx_reg +#define indirDX OP_REG, indir_dx_reg + +#define Sw OP_SEG, w_mode +#define Ap OP_DIR, lptr +#define Av OP_DIR, v_mode +#define Ob OP_OFF, b_mode +#define Ov OP_OFF, v_mode +#define Xb OP_DSSI, b_mode +#define Xv OP_DSSI, v_mode +#define Yb OP_ESDI, b_mode +#define Yv OP_ESDI, v_mode + +#define es OP_REG, es_reg +#define ss OP_REG, ss_reg +#define cs OP_REG, cs_reg +#define ds OP_REG, ds_reg +#define fs OP_REG, fs_reg +#define gs OP_REG, gs_reg + +#define MX OP_MMX, 0 +#define EM OP_EM, v_mode +#define MS OP_MS, b_mode + +typedef int (*op_rtn) PARAMS ((int bytemode, int aflag, int dflag)); + +static int OP_E PARAMS ((int, int, int)); +static int OP_G PARAMS ((int, int, int)); +static int OP_I PARAMS ((int, int, int)); +static int OP_indirE PARAMS ((int, int, int)); +static int OP_sI PARAMS ((int, int, int)); +static int OP_REG PARAMS ((int, int, int)); +static int OP_J PARAMS ((int, int, int)); +static int OP_DIR PARAMS ((int, int, int)); +static int OP_OFF PARAMS ((int, int, int)); +static int OP_ESDI PARAMS ((int, int, int)); +static int OP_DSSI PARAMS ((int, int, int)); +static int OP_SEG PARAMS ((int, int, int)); +static int OP_C PARAMS ((int, int, int)); +static int OP_D PARAMS ((int, int, int)); +static int OP_T PARAMS ((int, int, int)); +static int OP_rm PARAMS ((int, int, int)); +static int OP_ST PARAMS ((int, int, int)); +static int OP_STi PARAMS ((int, int, int)); +#if 0 +static int OP_ONE PARAMS ((int, int, int)); +#endif +static int OP_MMX PARAMS ((int, int, int)); +static int OP_EM PARAMS ((int, int, int)); +static int OP_MS PARAMS ((int, int, int)); + +static void append_prefix PARAMS ((void)); +static void set_op PARAMS ((int op)); +static void putop PARAMS ((char *template, int aflag, int dflag)); +static void dofloat PARAMS ((int aflag, int dflag)); +static int get16 PARAMS ((void)); +static int get32 PARAMS ((void)); +static void ckprefix PARAMS ((void)); + +#define b_mode 1 +#define v_mode 2 +#define w_mode 3 +#define d_mode 4 + +#define es_reg 100 +#define cs_reg 101 +#define ss_reg 102 +#define ds_reg 103 +#define fs_reg 104 +#define gs_reg 105 +#define eAX_reg 107 +#define eCX_reg 108 +#define eDX_reg 109 +#define eBX_reg 110 +#define eSP_reg 111 +#define eBP_reg 112 +#define eSI_reg 113 +#define eDI_reg 114 + +#define lptr 115 + +#define al_reg 116 +#define cl_reg 117 +#define dl_reg 118 +#define bl_reg 119 +#define ah_reg 120 +#define ch_reg 121 +#define dh_reg 122 +#define bh_reg 123 + +#define ax_reg 124 +#define cx_reg 125 +#define dx_reg 126 +#define bx_reg 127 +#define sp_reg 128 +#define bp_reg 129 +#define si_reg 130 +#define di_reg 131 + +#define indir_dx_reg 150 + +#define GRP1b NULL, NULL, 0 +#define GRP1S NULL, NULL, 1 +#define GRP1Ss NULL, NULL, 2 +#define GRP2b NULL, NULL, 3 +#define GRP2S NULL, NULL, 4 +#define GRP2b_one NULL, NULL, 5 +#define GRP2S_one NULL, NULL, 6 +#define GRP2b_cl NULL, NULL, 7 +#define GRP2S_cl NULL, NULL, 8 +#define GRP3b NULL, NULL, 9 +#define GRP3S NULL, NULL, 10 +#define GRP4 NULL, NULL, 11 +#define GRP5 NULL, NULL, 12 +#define GRP6 NULL, NULL, 13 +#define GRP7 NULL, NULL, 14 +#define GRP8 NULL, NULL, 15 +#define GRP9 NULL, NULL, 16 +#define GRP10 NULL, NULL, 17 +#define GRP11 NULL, NULL, 18 +#define GRP12 NULL, NULL, 19 + +#define FLOATCODE 50 +#define FLOAT NULL, NULL, FLOATCODE + +struct dis386 { + char *name; + op_rtn op1; + int bytemode1; + op_rtn op2; + int bytemode2; + op_rtn op3; + int bytemode3; +}; + +static struct dis386 dis386[] = { + /* 00 */ + { "addb", Eb, Gb }, + { "addS", Ev, Gv }, + { "addb", Gb, Eb }, + { "addS", Gv, Ev }, + { "addb", AL, Ib }, + { "addS", eAX, Iv }, + { "pushS", es }, + { "popS", es }, + /* 08 */ + { "orb", Eb, Gb }, + { "orS", Ev, Gv }, + { "orb", Gb, Eb }, + { "orS", Gv, Ev }, + { "orb", AL, Ib }, + { "orS", eAX, Iv }, + { "pushS", cs }, + { "(bad)" }, /* 0x0f extended opcode escape */ + /* 10 */ + { "adcb", Eb, Gb }, + { "adcS", Ev, Gv }, + { "adcb", Gb, Eb }, + { "adcS", Gv, Ev }, + { "adcb", AL, Ib }, + { "adcS", eAX, Iv }, + { "pushS", ss }, + { "popS", ss }, + /* 18 */ + { "sbbb", Eb, Gb }, + { "sbbS", Ev, Gv }, + { "sbbb", Gb, Eb }, + { "sbbS", Gv, Ev }, + { "sbbb", AL, Ib }, + { "sbbS", eAX, Iv }, + { "pushS", ds }, + { "popS", ds }, + /* 20 */ + { "andb", Eb, Gb }, + { "andS", Ev, Gv }, + { "andb", Gb, Eb }, + { "andS", Gv, Ev }, + { "andb", AL, Ib }, + { "andS", eAX, Iv }, + { "(bad)" }, /* SEG ES prefix */ + { "daa" }, + /* 28 */ + { "subb", Eb, Gb }, + { "subS", Ev, Gv }, + { "subb", Gb, Eb }, + { "subS", Gv, Ev }, + { "subb", AL, Ib }, + { "subS", eAX, Iv }, + { "(bad)" }, /* SEG CS prefix */ + { "das" }, + /* 30 */ + { "xorb", Eb, Gb }, + { "xorS", Ev, Gv }, + { "xorb", Gb, Eb }, + { "xorS", Gv, Ev }, + { "xorb", AL, Ib }, + { "xorS", eAX, Iv }, + { "(bad)" }, /* SEG SS prefix */ + { "aaa" }, + /* 38 */ + { "cmpb", Eb, Gb }, + { "cmpS", Ev, Gv }, + { "cmpb", Gb, Eb }, + { "cmpS", Gv, Ev }, + { "cmpb", AL, Ib }, + { "cmpS", eAX, Iv }, + { "(bad)" }, /* SEG DS prefix */ + { "aas" }, + /* 40 */ + { "incS", eAX }, + { "incS", eCX }, + { "incS", eDX }, + { "incS", eBX }, + { "incS", eSP }, + { "incS", eBP }, + { "incS", eSI }, + { "incS", eDI }, + /* 48 */ + { "decS", eAX }, + { "decS", eCX }, + { "decS", eDX }, + { "decS", eBX }, + { "decS", eSP }, + { "decS", eBP }, + { "decS", eSI }, + { "decS", eDI }, + /* 50 */ + { "pushS", eAX }, + { "pushS", eCX }, + { "pushS", eDX }, + { "pushS", eBX }, + { "pushS", eSP }, + { "pushS", eBP }, + { "pushS", eSI }, + { "pushS", eDI }, + /* 58 */ + { "popS", eAX }, + { "popS", eCX }, + { "popS", eDX }, + { "popS", eBX }, + { "popS", eSP }, + { "popS", eBP }, + { "popS", eSI }, + { "popS", eDI }, + /* 60 */ + { "pusha" }, + { "popa" }, + { "boundS", Gv, Ma }, + { "arpl", Ew, Gw }, + { "(bad)" }, /* seg fs */ + { "(bad)" }, /* seg gs */ + { "(bad)" }, /* op size prefix */ + { "(bad)" }, /* adr size prefix */ + /* 68 */ + { "pushS", Iv }, /* 386 book wrong */ + { "imulS", Gv, Ev, Iv }, + { "pushS", sIb }, /* push of byte really pushes 2 or 4 bytes */ + { "imulS", Gv, Ev, Ib }, + { "insb", Yb, indirDX }, + { "insS", Yv, indirDX }, + { "outsb", indirDX, Xb }, + { "outsS", indirDX, Xv }, + /* 70 */ + { "jo", Jb }, + { "jno", Jb }, + { "jb", Jb }, + { "jae", Jb }, + { "je", Jb }, + { "jne", Jb }, + { "jbe", Jb }, + { "ja", Jb }, + /* 78 */ + { "js", Jb }, + { "jns", Jb }, + { "jp", Jb }, + { "jnp", Jb }, + { "jl", Jb }, + { "jnl", Jb }, + { "jle", Jb }, + { "jg", Jb }, + /* 80 */ + { GRP1b }, + { GRP1S }, + { "(bad)" }, + { GRP1Ss }, + { "testb", Eb, Gb }, + { "testS", Ev, Gv }, + { "xchgb", Eb, Gb }, + { "xchgS", Ev, Gv }, + /* 88 */ + { "movb", Eb, Gb }, + { "movS", Ev, Gv }, + { "movb", Gb, Eb }, + { "movS", Gv, Ev }, + { "movw", Ew, Sw }, + { "leaS", Gv, M }, + { "movw", Sw, Ew }, + { "popS", Ev }, + /* 90 */ + { "nop" }, + { "xchgS", eCX, eAX }, + { "xchgS", eDX, eAX }, + { "xchgS", eBX, eAX }, + { "xchgS", eSP, eAX }, + { "xchgS", eBP, eAX }, + { "xchgS", eSI, eAX }, + { "xchgS", eDI, eAX }, + /* 98 */ + { "cWtS" }, + { "cStd" }, + { "lcall", Ap }, + { "(bad)" }, /* fwait */ + { "pushf" }, + { "popf" }, + { "sahf" }, + { "lahf" }, + /* a0 */ + { "movb", AL, Ob }, + { "movS", eAX, Ov }, + { "movb", Ob, AL }, + { "movS", Ov, eAX }, + { "movsb", Yb, Xb }, + { "movsS", Yv, Xv }, + { "cmpsb", Yb, Xb }, + { "cmpsS", Yv, Xv }, + /* a8 */ + { "testb", AL, Ib }, + { "testS", eAX, Iv }, + { "stosb", Yb, AL }, + { "stosS", Yv, eAX }, + { "lodsb", AL, Xb }, + { "lodsS", eAX, Xv }, + { "scasb", AL, Yb }, + { "scasS", eAX, Yv }, + /* b0 */ + { "movb", AL, Ib }, + { "movb", CL, Ib }, + { "movb", DL, Ib }, + { "movb", BL, Ib }, + { "movb", AH, Ib }, + { "movb", CH, Ib }, + { "movb", DH, Ib }, + { "movb", BH, Ib }, + /* b8 */ + { "movS", eAX, Iv }, + { "movS", eCX, Iv }, + { "movS", eDX, Iv }, + { "movS", eBX, Iv }, + { "movS", eSP, Iv }, + { "movS", eBP, Iv }, + { "movS", eSI, Iv }, + { "movS", eDI, Iv }, + /* c0 */ + { GRP2b }, + { GRP2S }, + { "ret", Iw }, + { "ret" }, + { "lesS", Gv, Mp }, + { "ldsS", Gv, Mp }, + { "movb", Eb, Ib }, + { "movS", Ev, Iv }, + /* c8 */ + { "enter", Iw, Ib }, + { "leave" }, + { "lret", Iw }, + { "lret" }, + { "int3" }, + { "int", Ib }, + { "into" }, + { "iret" }, + /* d0 */ + { GRP2b_one }, + { GRP2S_one }, + { GRP2b_cl }, + { GRP2S_cl }, + { "aam", Ib }, + { "aad", Ib }, + { "(bad)" }, + { "xlat" }, + /* d8 */ + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + { FLOAT }, + /* e0 */ + { "loopne", Jb }, + { "loope", Jb }, + { "loop", Jb }, + { "jCcxz", Jb }, + { "inb", AL, Ib }, + { "inS", eAX, Ib }, + { "outb", Ib, AL }, + { "outS", Ib, eAX }, + /* e8 */ + { "call", Av }, + { "jmp", Jv }, + { "ljmp", Ap }, + { "jmp", Jb }, + { "inb", AL, indirDX }, + { "inS", eAX, indirDX }, + { "outb", indirDX, AL }, + { "outS", indirDX, eAX }, + /* f0 */ + { "(bad)" }, /* lock prefix */ + { "(bad)" }, + { "(bad)" }, /* repne */ + { "(bad)" }, /* repz */ + { "hlt" }, + { "cmc" }, + { GRP3b }, + { GRP3S }, + /* f8 */ + { "clc" }, + { "stc" }, + { "cli" }, + { "sti" }, + { "cld" }, + { "std" }, + { GRP4 }, + { GRP5 }, +}; + +static struct dis386 dis386_twobyte[] = { + /* 00 */ + { GRP6 }, + { GRP7 }, + { "larS", Gv, Ew }, + { "lslS", Gv, Ew }, + { "(bad)" }, + { "(bad)" }, + { "clts" }, + { "(bad)" }, + /* 08 */ + { "invd" }, + { "wbinvd" }, + { "(bad)" }, { "ud2a" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 10 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 18 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 20 */ + /* these are all backward in appendix A of the intel book */ + { "movl", Rd, Cd }, + { "movl", Rd, Dd }, + { "movl", Cd, Rd }, + { "movl", Dd, Rd }, + { "movl", Rd, Td }, + { "(bad)" }, + { "movl", Td, Rd }, + { "(bad)" }, + /* 28 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 30 */ + { "wrmsr" }, { "rdtsc" }, { "rdmsr" }, { "rdpmc" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 38 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 40 */ + { "cmovo", Gv,Ev }, { "cmovno", Gv,Ev }, { "cmovb", Gv,Ev }, { "cmovae", Gv,Ev }, + { "cmove", Gv,Ev }, { "cmovne", Gv,Ev }, { "cmovbe", Gv,Ev }, { "cmova", Gv,Ev }, + /* 48 */ + { "cmovs", Gv,Ev }, { "cmovns", Gv,Ev }, { "cmovp", Gv,Ev }, { "cmovnp", Gv,Ev }, + { "cmovl", Gv,Ev }, { "cmovge", Gv,Ev }, { "cmovle", Gv,Ev }, { "cmovg", Gv,Ev }, + /* 50 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 58 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + /* 60 */ + { "punpcklbw", MX, EM }, + { "punpcklwd", MX, EM }, + { "punpckldq", MX, EM }, + { "packsswb", MX, EM }, + { "pcmpgtb", MX, EM }, + { "pcmpgtw", MX, EM }, + { "pcmpgtd", MX, EM }, + { "packuswb", MX, EM }, + /* 68 */ + { "punpckhbw", MX, EM }, + { "punpckhwd", MX, EM }, + { "punpckhdq", MX, EM }, + { "packssdw", MX, EM }, + { "(bad)" }, { "(bad)" }, + { "movd", MX, Ev }, + { "movq", MX, EM }, + /* 70 */ + { "(bad)" }, + { GRP10 }, + { GRP11 }, + { GRP12 }, + { "pcmpeqb", MX, EM }, + { "pcmpeqw", MX, EM }, + { "pcmpeqd", MX, EM }, + { "emms" }, + /* 78 */ + { "(bad)" }, { "(bad)" }, { "(bad)" }, { "(bad)" }, + { "(bad)" }, { "(bad)" }, + { "movd", Ev, MX }, + { "movq", EM, MX }, + /* 80 */ + { "jo", Jv }, + { "jno", Jv }, + { "jb", Jv }, + { "jae", Jv }, + { "je", Jv }, + { "jne", Jv }, + { "jbe", Jv }, + { "ja", Jv }, + /* 88 */ + { "js", Jv }, + { "jns", Jv }, + { "jp", Jv }, + { "jnp", Jv }, + { "jl", Jv }, + { "jge", Jv }, + { "jle", Jv }, + { "jg", Jv }, + /* 90 */ + { "seto", Eb }, + { "setno", Eb }, + { "setb", Eb }, + { "setae", Eb }, + { "sete", Eb }, + { "setne", Eb }, + { "setbe", Eb }, + { "seta", Eb }, + /* 98 */ + { "sets", Eb }, + { "setns", Eb }, + { "setp", Eb }, + { "setnp", Eb }, + { "setl", Eb }, + { "setge", Eb }, + { "setle", Eb }, + { "setg", Eb }, + /* a0 */ + { "pushS", fs }, + { "popS", fs }, + { "cpuid" }, + { "btS", Ev, Gv }, + { "shldS", Ev, Gv, Ib }, + { "shldS", Ev, Gv, CL }, + { "(bad)" }, + { "(bad)" }, + /* a8 */ + { "pushS", gs }, + { "popS", gs }, + { "rsm" }, + { "btsS", Ev, Gv }, + { "shrdS", Ev, Gv, Ib }, + { "shrdS", Ev, Gv, CL }, + { "(bad)" }, + { "imulS", Gv, Ev }, + /* b0 */ + { "cmpxchgb", Eb, Gb }, + { "cmpxchgS", Ev, Gv }, + { "lssS", Gv, Mp }, /* 386 lists only Mp */ + { "btrS", Ev, Gv }, + { "lfsS", Gv, Mp }, /* 386 lists only Mp */ + { "lgsS", Gv, Mp }, /* 386 lists only Mp */ + { "movzbS", Gv, Eb }, + { "movzwS", Gv, Ew }, + /* b8 */ + { "ud2b" }, + { "(bad)" }, + { GRP8 }, + { "btcS", Ev, Gv }, + { "bsfS", Gv, Ev }, + { "bsrS", Gv, Ev }, + { "movsbS", Gv, Eb }, + { "movswS", Gv, Ew }, + /* c0 */ + { "xaddb", Eb, Gb }, + { "xaddS", Ev, Gv }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { GRP9 }, + /* c8 */ + { "bswap", eAX }, + { "bswap", eCX }, + { "bswap", eDX }, + { "bswap", eBX }, + { "bswap", eSP }, + { "bswap", eBP }, + { "bswap", eSI }, + { "bswap", eDI }, + /* d0 */ + { "(bad)" }, + { "psrlw", MX, EM }, + { "psrld", MX, EM }, + { "psrlq", MX, EM }, + { "(bad)" }, + { "pmullw", MX, EM }, + { "(bad)" }, { "(bad)" }, + /* d8 */ + { "psubusb", MX, EM }, + { "psubusw", MX, EM }, + { "(bad)" }, + { "pand", MX, EM }, + { "paddusb", MX, EM }, + { "paddusw", MX, EM }, + { "(bad)" }, + { "pandn", MX, EM }, + /* e0 */ + { "(bad)" }, + { "psraw", MX, EM }, + { "psrad", MX, EM }, + { "(bad)" }, + { "(bad)" }, + { "pmulhw", MX, EM }, + { "(bad)" }, { "(bad)" }, + /* e8 */ + { "psubsb", MX, EM }, + { "psubsw", MX, EM }, + { "(bad)" }, + { "por", MX, EM }, + { "paddsb", MX, EM }, + { "paddsw", MX, EM }, + { "(bad)" }, + { "pxor", MX, EM }, + /* f0 */ + { "(bad)" }, + { "psllw", MX, EM }, + { "pslld", MX, EM }, + { "psllq", MX, EM }, + { "(bad)" }, + { "pmaddwd", MX, EM }, + { "(bad)" }, { "(bad)" }, + /* f8 */ + { "psubb", MX, EM }, + { "psubw", MX, EM }, + { "psubd", MX, EM }, + { "(bad)" }, + { "paddb", MX, EM }, + { "paddw", MX, EM }, + { "paddd", MX, EM }, + { "(bad)" } +}; + +static const unsigned char onebyte_has_modrm[256] = { + 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, + 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, + 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, + 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, + 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1 +}; + +static const unsigned char twobyte_has_modrm[256] = { + /* 00 */ 1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */ + /* 10 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */ + /* 20 */ 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, /* 2f */ + /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */ + /* 40 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4f */ + /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 5f */ + /* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1, /* 6f */ + /* 70 */ 0,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1, /* 7f */ + /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */ + /* 90 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 9f */ + /* a0 */ 0,0,0,1,1,1,1,1,0,0,0,1,1,1,1,1, /* af */ + /* b0 */ 1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1, /* bf */ + /* c0 */ 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, /* cf */ + /* d0 */ 0,1,1,1,0,1,0,0,1,1,0,1,1,1,0,1, /* df */ + /* e0 */ 0,1,1,0,0,1,0,0,1,1,0,1,1,1,0,1, /* ef */ + /* f0 */ 0,1,1,1,0,1,0,0,1,1,1,0,1,1,1,0 /* ff */ +}; + +static char obuf[100]; +static char *obufp; +static char scratchbuf[100]; +static unsigned char *start_codep; +static unsigned char *codep; +static disassemble_info *the_info; +static int mod; +static int rm; +static int reg; +static void oappend PARAMS ((char *s)); + +static char *names32[]={ + "%eax","%ecx","%edx","%ebx", "%esp","%ebp","%esi","%edi", +}; +static char *names16[] = { + "%ax","%cx","%dx","%bx","%sp","%bp","%si","%di", +}; +static char *names8[] = { + "%al","%cl","%dl","%bl","%ah","%ch","%dh","%bh", +}; +static char *names_seg[] = { + "%es","%cs","%ss","%ds","%fs","%gs","%?","%?", +}; +static char *index16[] = { + "bx+si","bx+di","bp+si","bp+di","si","di","bp","bx" +}; + +static struct dis386 grps[][8] = { + /* GRP1b */ + { + { "addb", Eb, Ib }, + { "orb", Eb, Ib }, + { "adcb", Eb, Ib }, + { "sbbb", Eb, Ib }, + { "andb", Eb, Ib }, + { "subb", Eb, Ib }, + { "xorb", Eb, Ib }, + { "cmpb", Eb, Ib } + }, + /* GRP1S */ + { + { "addS", Ev, Iv }, + { "orS", Ev, Iv }, + { "adcS", Ev, Iv }, + { "sbbS", Ev, Iv }, + { "andS", Ev, Iv }, + { "subS", Ev, Iv }, + { "xorS", Ev, Iv }, + { "cmpS", Ev, Iv } + }, + /* GRP1Ss */ + { + { "addS", Ev, sIb }, + { "orS", Ev, sIb }, + { "adcS", Ev, sIb }, + { "sbbS", Ev, sIb }, + { "andS", Ev, sIb }, + { "subS", Ev, sIb }, + { "xorS", Ev, sIb }, + { "cmpS", Ev, sIb } + }, + /* GRP2b */ + { + { "rolb", Eb, Ib }, + { "rorb", Eb, Ib }, + { "rclb", Eb, Ib }, + { "rcrb", Eb, Ib }, + { "shlb", Eb, Ib }, + { "shrb", Eb, Ib }, + { "(bad)" }, + { "sarb", Eb, Ib }, + }, + /* GRP2S */ + { + { "rolS", Ev, Ib }, + { "rorS", Ev, Ib }, + { "rclS", Ev, Ib }, + { "rcrS", Ev, Ib }, + { "shlS", Ev, Ib }, + { "shrS", Ev, Ib }, + { "(bad)" }, + { "sarS", Ev, Ib }, + }, + /* GRP2b_one */ + { + { "rolb", Eb }, + { "rorb", Eb }, + { "rclb", Eb }, + { "rcrb", Eb }, + { "shlb", Eb }, + { "shrb", Eb }, + { "(bad)" }, + { "sarb", Eb }, + }, + /* GRP2S_one */ + { + { "rolS", Ev }, + { "rorS", Ev }, + { "rclS", Ev }, + { "rcrS", Ev }, + { "shlS", Ev }, + { "shrS", Ev }, + { "(bad)" }, + { "sarS", Ev }, + }, + /* GRP2b_cl */ + { + { "rolb", Eb, CL }, + { "rorb", Eb, CL }, + { "rclb", Eb, CL }, + { "rcrb", Eb, CL }, + { "shlb", Eb, CL }, + { "shrb", Eb, CL }, + { "(bad)" }, + { "sarb", Eb, CL }, + }, + /* GRP2S_cl */ + { + { "rolS", Ev, CL }, + { "rorS", Ev, CL }, + { "rclS", Ev, CL }, + { "rcrS", Ev, CL }, + { "shlS", Ev, CL }, + { "shrS", Ev, CL }, + { "(bad)" }, + { "sarS", Ev, CL } + }, + /* GRP3b */ + { + { "testb", Eb, Ib }, + { "(bad)", Eb }, + { "notb", Eb }, + { "negb", Eb }, + { "mulb", AL, Eb }, + { "imulb", AL, Eb }, + { "divb", AL, Eb }, + { "idivb", AL, Eb } + }, + /* GRP3S */ + { + { "testS", Ev, Iv }, + { "(bad)" }, + { "notS", Ev }, + { "negS", Ev }, + { "mulS", eAX, Ev }, + { "imulS", eAX, Ev }, + { "divS", eAX, Ev }, + { "idivS", eAX, Ev }, + }, + /* GRP4 */ + { + { "incb", Eb }, + { "decb", Eb }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + }, + /* GRP5 */ + { + { "incS", Ev }, + { "decS", Ev }, + { "call", indirEv }, + { "lcall", indirEv }, + { "jmp", indirEv }, + { "ljmp", indirEv }, + { "pushS", Ev }, + { "(bad)" }, + }, + /* GRP6 */ + { + { "sldt", Ew }, + { "str", Ew }, + { "lldt", Ew }, + { "ltr", Ew }, + { "verr", Ew }, + { "verw", Ew }, + { "(bad)" }, + { "(bad)" } + }, + /* GRP7 */ + { + { "sgdt", Ew }, + { "sidt", Ew }, + { "lgdt", Ew }, + { "lidt", Ew }, + { "smsw", Ew }, + { "(bad)" }, + { "lmsw", Ew }, + { "invlpg", Ew }, + }, + /* GRP8 */ + { + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "btS", Ev, Ib }, + { "btsS", Ev, Ib }, + { "btrS", Ev, Ib }, + { "btcS", Ev, Ib }, + }, + /* GRP9 */ + { + { "(bad)" }, + { "cmpxchg8b", Ev }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + }, + /* GRP10 */ + { + { "(bad)" }, + { "(bad)" }, + { "psrlw", MS, Ib }, + { "(bad)" }, + { "psraw", MS, Ib }, + { "(bad)" }, + { "psllw", MS, Ib }, + { "(bad)" }, + }, + /* GRP11 */ + { + { "(bad)" }, + { "(bad)" }, + { "psrld", MS, Ib }, + { "(bad)" }, + { "psrad", MS, Ib }, + { "(bad)" }, + { "pslld", MS, Ib }, + { "(bad)" }, + }, + /* GRP12 */ + { + { "(bad)" }, + { "(bad)" }, + { "psrlq", MS, Ib }, + { "(bad)" }, + { "(bad)" }, + { "(bad)" }, + { "psllq", MS, Ib }, + { "(bad)" }, + } +}; + +#define PREFIX_REPZ 1 +#define PREFIX_REPNZ 2 +#define PREFIX_LOCK 4 +#define PREFIX_CS 8 +#define PREFIX_SS 0x10 +#define PREFIX_DS 0x20 +#define PREFIX_ES 0x40 +#define PREFIX_FS 0x80 +#define PREFIX_GS 0x100 +#define PREFIX_DATA 0x200 +#define PREFIX_ADR 0x400 +#define PREFIX_FWAIT 0x800 + +static int prefixes; + +static void +ckprefix () +{ + prefixes = 0; + while (1) + { + FETCH_DATA (the_info, codep + 1); + switch (*codep) + { + case 0xf3: + prefixes |= PREFIX_REPZ; + break; + case 0xf2: + prefixes |= PREFIX_REPNZ; + break; + case 0xf0: + prefixes |= PREFIX_LOCK; + break; + case 0x2e: + prefixes |= PREFIX_CS; + break; + case 0x36: + prefixes |= PREFIX_SS; + break; + case 0x3e: + prefixes |= PREFIX_DS; + break; + case 0x26: + prefixes |= PREFIX_ES; + break; + case 0x64: + prefixes |= PREFIX_FS; + break; + case 0x65: + prefixes |= PREFIX_GS; + break; + case 0x66: + prefixes |= PREFIX_DATA; + break; + case 0x67: + prefixes |= PREFIX_ADR; + break; + case 0x9b: + prefixes |= PREFIX_FWAIT; + break; + default: + return; + } + codep++; + } +} + +static char op1out[100], op2out[100], op3out[100]; +static int op_address[3], op_ad, op_index[3]; +static int start_pc; + + +/* + * On the 386's of 1988, the maximum length of an instruction is 15 bytes. + * (see topic "Redundant prefixes" in the "Differences from 8086" + * section of the "Virtual 8086 Mode" chapter.) + * 'pc' should be the address of this instruction, it will + * be used to print the target address if this is a relative jump or call + * The function returns the length of this instruction in bytes. + */ + +int print_insn_x86 PARAMS ((bfd_vma pc, disassemble_info *info, int aflag, + int dflag)); +int +print_insn_i386 (pc, info) + bfd_vma pc; + disassemble_info *info; +{ + if (info->mach == bfd_mach_i386_i386) + return print_insn_x86 (pc, info, 1, 1); + else if (info->mach == bfd_mach_i386_i8086) + return print_insn_x86 (pc, info, 0, 0); + else +#if defined(__KERNEL__) + printk("Bad machine type\n"); +#else + abort (); +#endif + 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; +} Index: 2.2.15pre19/arch/i386/kdb/kd_id.c --- 2.2.15pre19/arch/i386/kdb/kd_id.c Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/arch/i386/kdb/kd_id.c Fri, 21 Apr 2000 18:04:41 +1000 kaos (linux-2.2/s/c/49_kd_id.c 1.2 644) @@ -0,0 +1,268 @@ +/* + * Kernel Debugger. + * + * This module parses the instruction disassembly (id) command + * and invokes the disassembler. + * + * Copyright 1999, Silicon Graphics, Inc. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + */ + +#if defined(STANDALONE) +#include +#include +#include +#include +#include "kdb.h" +#include "sakdbsupport.h" +#include "dis-asm.h" +#else +#include +#include +#include +#include +#include +#include "dis-asm.h" +#include "kdbsupport.h" +#endif + +static disassemble_info kdb_di; + +/* ARGSUSED */ +static int +kdbgetsym(bfd_vma addr, struct disassemble_info *dip) +{ + + return 0; +} + +/* ARGSUSED */ +static void +kdbprintintaddr(bfd_vma addr, struct disassemble_info *dip, int flag) +{ + __ksymtab_t symtab; + int found = kdbnearsym(addr, &symtab); + + /* + * Print a symbol name or address as necessary. + */ + if (found) { + dip->fprintf_func(dip->stream, "%s", 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, ": "); +} + +static void +kdbprintaddr(bfd_vma addr, struct disassemble_info *dip) +{ + kdbprintintaddr(addr, dip, 0); +} + +/* ARGSUSED */ +static int +kdbgetidmem(bfd_vma addr, bfd_byte *buf, int length, struct disassemble_info *dip) +{ + bfd_byte *bp = buf; + int i; + + /* + * Fill the provided buffer with bytes from + * memory, starting at address 'addr' for 'length bytes. + * + */ + + for(i=0; i + * + * Parameters: + * argc Count of arguments in argv + * argv Space delimited command line arguments + * envp Environment value + * regs Exception frame at entry to kernel debugger + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic if failure. + * Locking: + * None. + * Remarks: + */ + +int +kdb_id(int argc, const char **argv, const char **envp, struct pt_regs* regs) +{ + unsigned long pc; + int icount; + int diag; + int i; + char * mode; + int nextarg; + long offset = 0; + static unsigned long lastpc=0; + + if (argc != 1) { + if (lastpc == 0) { + return KDB_ARGCOUNT; + } else { + char lastbuf[50]; + sprintf(lastbuf, "0x%lx", lastpc); + argv[1] = lastbuf; + argc = 1; + } + } + + /* + * Fetch PC. First, check to see if it is a symbol, if not, + * try address. + */ + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &pc, &offset, NULL, regs); + if (diag) + return diag; + + /* + * Number of lines to display + */ + diag = kdbgetintenv("IDCOUNT", &icount); + if (diag) + return diag; + + mode = kdbgetenv("IDMODE"); + if (mode) { + if (strcmp(mode, "x86") == 0) { + kdb_di.mach = bfd_mach_i386_i386; + } else if (strcmp(mode, "8086") == 0) { + kdb_di.mach = bfd_mach_i386_i8086; + } else { + return KDB_BADMODE; + } + } + + for(i=0; i +#include +#include +#include +#include +#if defined(__SMP__) +#include +#endif +#include +#include +#include "kdbsupport.h" +#include "kdb_io.h" + +volatile int kdb_active = 0; +volatile int kdb_flags = 0; +spinlock_t kdb_lock = SPIN_LOCK_UNLOCKED; +int kdb_nextline = 1; +int kdb_new_cpu = -1; + +kdb_jmp_buf kdbjmpbuf[NR_CPUS]; + + /* + * Describe the command table. + */ +typedef struct _kdbtab { + char *cmd_name; /* Command name */ + kdb_func cmd_func; /* Function to execute command */ + char *cmd_usage; /* Usage String for this command */ + char *cmd_help; /* Help message for this command */ + short cmd_flags; /* Parsing flags */ + short cmd_minlen; /* Minimum legal # command chars required */ +} kdbtab_t; + + /* + * Provide space for KDB_MAX_COMMANDS commands. + */ +#define KDB_MAX_COMMANDS 100 + +static kdbtab_t kdb_commands[KDB_MAX_COMMANDS]; + + /* + * Error messages. XXX - use a macro for this... + */ +static char kdb_notfound[] = "Command Not Found"; +static char kdb_argcount[] = "Improper argument count, see usage."; +static char kdb_badwidth[] = "Illegal value for BYTESPERWORD use 1, 2 or 4"; +static char kdb_badradix[] = "Illegal value for RADIX use 8, 10 or 16"; +static char kdb_notenv[] = "Cannot find environment variable"; +static char kdb_noenvvalue[] = "Environment variable should have value"; +static char kdb_notimp[] = "Command not implemented"; +static char kdb_envfull[] = "Environment full"; +static char kdb_envbuffull[] = "Environment buffer full"; +static char kdb_toomanybpt[] = "Too many breakpoints defined"; +static char kdb_toomanydbregs[] = "More breakpoints than db registers defined"; +static char kdb_dupbpt[] = "Duplicate breakpoint address"; +static char kdb_bptnotfound[] = "Breakpoint not found"; +static char kdb_badmode[] = "IDMODE should be x86 or 8086"; +static char kdb_badint[] = "Illegal numeric value"; +static char kdb_invaddrfmt[] = "Invalid symbolic address format"; +static char kdb_badreg[] = "Invalid register name"; +static char kdb_badcpunum[] = "Invalid cpu number"; +static char kdb_badlength[] = "Invalid length field"; +static char kdb_nobp[] = "No Breakpoint exists"; + +static char *kdb_messages[] = { + (char *)0, + kdb_notfound, + (char *)0, + kdb_argcount, + kdb_badwidth, + kdb_badradix, + kdb_notenv, + kdb_noenvvalue, + kdb_notimp, + kdb_envfull, + kdb_envbuffull, + kdb_toomanybpt, + kdb_toomanydbregs, + kdb_dupbpt, + kdb_bptnotfound, + kdb_badmode, + kdb_badint, + kdb_invaddrfmt, + kdb_badreg, + (char *)0, /* KDB_CPUSWITCH */ + kdb_badcpunum, + kdb_badlength, + kdb_nobp, + (char *)0 +}; +static const int __nkdb_err = sizeof(kdb_messages) / sizeof(char *); + + +/* + * Initial environment. This is all kept static and local to + * this file. We don't want to rely on the memory allocation + * mechanisms in the kernel, so we use a very limited allocate-only + * heap for new and altered environment variables. The entire + * environment is limited to a fixed number of entries (add more + * to __env[] if required) and a fixed amount of heap (add more to + * KDB_ENVBUFSIZE if required). + */ + +#if defined(CONFIG_SMP) +static char prompt_str[] = "PROMPT=[%d]kdb> "; +static char more_str[] = "MOREPROMPT=[%d]more> "; +#else +static char prompt_str[] = "PROMPT=kdb> "; +static char more_str[] = "MOREPROMPT=more> "; +#endif +static char radix_str[] = "RADIX=16"; +static char lines_str[] = "LINES=24"; +static char columns_str[] = "COLUMNS=80"; +static char mdcount_str[] = "MDCOUNT=8"; /* lines of md output */ +static char idcount_str[] = "IDCOUNT=16"; /* lines of id output */ +static char bperword_str[] = "BYTESPERWORD=4"; /* Word size for md output */ +static char idmode_str[] = "IDMODE=x86"; /* 32-bit mode */ +static char btargs_str[] = "BTARGS=5"; /* 5 possible args in bt */ +static char sscount_str[] = "SSCOUNT=20"; /* lines of ssb output */ + +static char *__env[] = { + bperword_str, + columns_str, + idcount_str, + lines_str, + mdcount_str, + prompt_str, + radix_str, + idmode_str, + btargs_str, + sscount_str, + more_str, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0 +}; +static const int __nenv = (sizeof(__env) / sizeof(char *)); + +/* + * kdbgetenv + * + * This function will return the character string value of + * an environment variable. + * + * Parameters: + * match A character string representing an environment variable. + * Outputs: + * None. + * Returns: + * NULL No environment variable matches 'match' + * char* Pointer to string value of environment variable. + * Locking: + * No locking considerations required. + * Remarks: + */ +char * +kdbgetenv(const char *match) +{ + char **ep = __env; + int matchlen = strlen(match); + int i; + + for(i=0; i<__nenv; i++) { + char *e = *ep++; + + if (!e) continue; + + if ((strncmp(match, e, matchlen) == 0) + && ((e[matchlen] == '\0') + ||(e[matchlen] == '='))) { + char *cp = strchr(e, '='); + return (cp)?++cp:""; + } + } + return (char *)0; +} + +/* + * kdballocenv + * + * This function is used to allocate bytes for environment entries. + * + * Parameters: + * match A character string representing a numeric value + * Outputs: + * *value the unsigned long represntation of the env variable 'match' + * Returns: + * Zero on success, a kdb diagnostic on failure. + * Locking: + * No locking considerations required. Must be called with all + * processors halted. + * Remarks: + * We use a static environment buffer (envbuffer) to hold the values + * of dynamically generated environment variables (see kdb_set). Buffer + * space once allocated is never free'd, so over time, the amount of space + * (currently 512 bytes) will be exhausted if env variables are changed + * frequently. + */ +static char * +kdballocenv(size_t bytes) +{ +#define KDB_ENVBUFSIZE 512 + static char envbuffer[KDB_ENVBUFSIZE]; + static int envbufsize = 0; + char *ep = (char *)0; + + if ((KDB_ENVBUFSIZE - envbufsize) >= bytes) { + ep = &envbuffer[envbufsize]; + envbufsize += bytes; + } + return ep; +} + +/* + * kdbgetulenv + * + * This function will return the value of an unsigned long-valued + * environment variable. + * + * Parameters: + * match A character string representing a numeric value + * Outputs: + * *value the unsigned long represntation of the env variable 'match' + * Returns: + * Zero on success, a kdb diagnostic on failure. + * Locking: + * No locking considerations required. + * Remarks: + */ + +int +kdbgetulenv(const char *match, unsigned long *value) +{ + char *ep; + + ep = kdbgetenv(match); + if (!ep) return KDB_NOTENV; + if (strlen(ep) == 0) return KDB_NOENVVALUE; + + *value = simple_strtoul(ep, 0, 0); + + return 0; +} + +/* + * kdbgetintenv + * + * This function will return the value of an integer-valued + * environment variable. + * + * Parameters: + * match A character string representing an integer-valued env variable + * Outputs: + * *value the integer representation of the environment variable 'match' + * Returns: + * Zero on success, a kdb diagnostic on failure. + * Locking: + * No locking considerations required. + * Remarks: + */ + +int +kdbgetintenv(const char *match, int *value) { + unsigned long val; + int diag; + + diag = kdbgetulenv(match, &val); + if (!diag) { + *value = (int) val; + } + return diag; +} + +/* + * kdbgetularg + * + * This function will convert a numeric string + * into an unsigned long value. + * + * Parameters: + * arg A character string representing a numeric value + * Outputs: + * *value the unsigned long represntation of arg. + * Returns: + * Zero on success, a kdb diagnostic on failure. + * Locking: + * No locking considerations required. + * Remarks: + */ + +int +kdbgetularg(const char *arg, unsigned long *value) +{ + char *endp; + unsigned long val; + + val = simple_strtoul(arg, &endp, 0); + + if (endp == arg) { + /* + * Try base 16, for us folks too lazy to type the + * leading 0x... + */ + val = simple_strtoul(arg, &endp, 16); + if (endp == arg) + return KDB_BADINT; + } + + *value = val; + + return 0; +} + +/* + * kdbgetaddrarg + * + * This function is responsible for parsing an + * address-expression and returning the value of + * the expression, symbol name, and offset to the caller. + * + * The argument may consist of a numeric value (decimal or + * hexidecimal), a symbol name, a register name (preceeded + * by the percent sign), an environment variable with a numeric + * value (preceeded by a dollar sign) or a simple arithmetic + * expression consisting of a symbol name, +/-, and a numeric + * constant value (offset). + * + * Parameters: + * argc - count of arguments in argv + * argv - argument vector + * *nextarg - index to next unparsed argument in argv[] + * + * Outputs: + * *value - receives the value of the address-expression + * *offset - receives the offset specified, if any + * *name - receives the symbol name, if any + * *nextarg - index to next unparsed argument in argv[] + * + * Returns: + * zero is returned on success, a kdb diagnostic code is + * returned on error. + * + * Locking: + * No locking requirements. + * + * Remarks: + * + */ + +int +kdbgetaddrarg(int argc, const char **argv, int *nextarg, + unsigned long *value, long *offset, + char **name, struct pt_regs *regs) +{ + unsigned long addr; + long off = 0; + int positive; + int diag; + int found = 0; + char *symname; + char symbol = '\0'; + char *cp; + __ksymtab_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 = kdbgetregcontents(&symname[1], regs, &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) +{ + if (diag >= 0) { + kdb_printf("no error detected\n"); + return; + } + diag = -diag; + if (diag >= __nkdb_err) { + kdb_printf("Illegal diag %d\n", -diag); + return; + } + kdb_printf("diag: %d: %s\n", diag, kdb_messages[diag]); +} + +/* + * kdb_parse + * + * Parse the command line, search the command table for a + * matching command and invoke the command function. + * + * Parameters: + * cmdstr The input command line to be parsed. + * regs The registers at the time kdb was entered. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic if failure. + * Locking: + * None. + * Remarks: + * Limited to 20 tokens. + * + * Real rudimentary tokenization. Basically only whitespace + * is considered a token delimeter (but special consideration + * is taken of the '=' sign as used by the 'set' command). + * + * The algorithm used to tokenize the input string relies on + * there being at least one whitespace (or otherwise useless) + * character between tokens as the character immediately following + * the token is altered in-place to a null-byte to terminate the + * token string. + */ + +#define MAXARGC 20 + +static int +kdb_parse(char *cmdstr, struct pt_regs *regs) +{ + char *argv[MAXARGC]; + int argc=0; + char *cp; + kdbtab_t *tp; + int i; + + /* + * First tokenize the command string. + */ + cp = cmdstr; + + /* + * If a null statement is provided, do nothing. + */ + if ((*cp == '\n') || (*cp == '\0')) + return 0; + + while (*cp) { + /* skip whitespace */ + while (isspace(*cp)) cp++; + if ((*cp == '\0') || (*cp == '\n')) + break; + argv[argc++] = cp; + /* Skip to next whitespace */ + for(; *cp && (!isspace(*cp) && (*cp != '=')); cp++); + *cp++ = '\0'; /* Squash a ws or '=' character */ + } + + for(tp=kdb_commands, i=0; i < KDB_MAX_COMMANDS; i++,tp++) { + if (tp->cmd_name) { + /* + * If this command is allowed to be abbreviated, + * check to see if this is it. + */ + + if (tp->cmd_minlen + && (strlen(argv[0]) <= tp->cmd_minlen)) { + if (strncmp(argv[0], + tp->cmd_name, + tp->cmd_minlen) == 0) { + break; + } + } + + if (strcmp(argv[0], tp->cmd_name)==0) { + break; + } + } + } + + if (i < KDB_MAX_COMMANDS) { + return (*tp->cmd_func)(argc-1, + (const char**)argv, + (const char**)__env, + regs); + } + + /* + * If the input with which we were presented does not + * map to an existing command, attempt to parse it as an + * address argument and display the result. Useful for + * obtaining the address of a variable, or the nearest symbol + * to an address contained in a register. + */ + { + unsigned long value; + char *name = NULL; + long offset; + int nextarg = 0; + + if (kdbgetaddrarg(0, (const char **)argv, &nextarg, + &value, &offset, &name, regs)) { + return KDB_NOTFOUND; + } + + kdb_printf("%s = 0x%8.8x ", argv[0], value); + if (name) { + kdb_printf("(%s+0x%lx)", name, offset); + } + kdb_printf("\n"); + return 0; + } +} + +/* + * kdb + * + * This function is the entry point for the kernel debugger. It + * provides a command parser and associated support functions to + * allow examination and control of an active kernel. + * + * This function may be invoked directly from any + * point in the kernel by calling with reason == KDB_REASON_ENTER + * (XXX - note that the regs aren't set up this way - could + * use a software interrupt to enter kdb to get regs...) + * + * The breakpoint trap code should invoke this function with + * one of KDB_REASON_BREAK (int 03) or KDB_REASON_DEBUG (debug register) + * + * the panic function should invoke this function with + * KDB_REASON_PANIC. + * + * The kernel fault handler should invoke this function with + * reason == KDB_REASON_FAULT and error == trap vector #. + * + * Inputs: + * reason The reason KDB was invoked + * error The hardware-defined error code + * regs The registers at time of fault/breakpoint + * Outputs: + * none + * Locking: + * none + * Remarks: + * No assumptions of system state. This function may be invoked + * with arbitrary locks held. It will stop all other processors + * in an SMP environment, disable all interrupts and does not use + * the operating systems keyboard driver. + */ + +int +kdb(int reason, int error, struct pt_regs *regs) +{ + char cmdbuf[255]; + char *cmd; + int diag; + unsigned long flags; + struct pt_regs func_regs; + + /* + * Disable processor interrupts during kdb command processing + */ + __save_flags(flags); + __cli(); + + /* + * 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 should take them into kdb through the back door. Once + * the initial processor releases the debugger, the rest of + * the processors will race for it. + */ + + /* + * First, remove the breakpoints both to prevent double-faults + * if kdb happens to use a function where a breakpoint + * has been enabled, as well as so that the breakpoint register(s) + * will get restored correctly on other processors after they + * leave the kernel debugger serialization barrier. + */ + + kdb_bp_remove(); + + 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); + kdb_new_cpu = -1; + } else if (!kdb_active) { + printk("kdb: CPU switch without kdb_active, I'm confused\n"); + } + + if (reason != KDB_REASON_DEBUG) { + kdb_printf("Entering kdb "); +#if defined(__SMP__) + kdb_printf("on processor %d ", smp_processor_id()); +#endif + } + + switch (reason) { + case KDB_REASON_DEBUG: + /* + * If re-entering kdb after a single step + * command, don't print the message. + */ + diag = kdb_db_trap(regs); + if (diag == 0) { + kdb_printf("Entering kdb "); +#if defined(__SMP__) + kdb_printf("on processor %d ", smp_processor_id()); +#endif + } else if (diag == 2) { + /* + * in middle of ssb command. Just return. + */ + + kdb_active = 0; + __restore_flags(flags); + return 0; + } else if (diag == 3) { + /* + * Called due to non-existant breakpoint. This can + * happen if a breakpoint is cleared with the 'bc' + * command while other processors are queueud up + * waiting to enter kdb(). + * + * Ignore it, return, clear kdb_active. + */ + kdb_active = 0; + __restore_flags(flags); + return 0; + } + break; + case KDB_REASON_FAULT: + break; + case KDB_REASON_INT: + kdb_printf("due to KDB_ENTER() call\n"); + break; + case KDB_REASON_KEYBOARD: + kdb_printf("due to Keyboard Entry\n"); + break; + case KDB_REASON_SWITCH: + kdb_printf("due to cpu switch\n"); + break; + case KDB_REASON_ENTER: + kdb_printf("due to function call\n"); + regs = &func_regs; + regs->xcs = 0; +#if defined(CONFIG_KDB_FRAMEPTR) + asm volatile("movl %%ebp,%0":"=m" (*(int *)®s->ebp)); +#endif + asm volatile("movl %%esp,%0":"=m" (*(int *)®s->esp)); + regs->eip = (long) &kdb; /* for traceback. */ + break; + case KDB_REASON_PANIC: + kdb_printf("due to panic @ 0x%8.8x\n", regs->eip); + kdbdumpregs(regs, NULL, NULL); + break; + case KDB_REASON_NMI: + kdb_printf("due to NonMaskable Interrupt @ 0x%8.8x\n", + regs->eip); + kdbdumpregs(regs, NULL, NULL); + break; + case KDB_REASON_BREAK: + kdb_printf("due to Breakpoint @ 0x%8.8x\n", regs->eip); + break; + default: + break; + } + +#if defined(__SMP__) + /* + * If SMP, stop other processors + */ + if ((reason != KDB_REASON_SWITCH) + && (smp_num_cpus > 1)) { + /* + * Stop all other processors + */ + smp_kdb_stop(1); + } +#endif /* __SMP__ */ + + 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 = kbd_getstr(cmdbuf, sizeof(cmdbuf), kdbgetenv("PROMPT")); + + diag = kdb_parse(cmd, regs); + if (diag == KDB_NOTFOUND) { + kdb_printf("Unknown kdb command: '%s'\n", cmd); + diag = 0; + } + if ((diag == KDB_GO) + || (diag == KDB_CPUSWITCH)) + break; /* Go or cpu switch command */ + + if (diag) + kdb_cmderror(diag); + } + +#if defined(__SMP__) + if ((diag == KDB_CPUSWITCH) + && (kdb_new_cpu != -1)) { + /* + * Leaving the other CPU's at the barrier, except the + * one we are switching to, we'll come to the barrier + * until released by the 'cpu' or 'go' commands. + */ + + set_bit(smp_processor_id(), &smp_kdb_wait); + /* + * let the new cpu go. + */ + clear_bit(kdb_new_cpu, &smp_kdb_wait); + + while (test_bit(smp_processor_id(), &smp_kdb_wait)) + ; + + if (kdb_new_cpu == smp_processor_id()) { + /* + * Back off the stack and revert to called location. + */ + kdb_longjmp(&kdbjmpbuf[smp_processor_id()], 2); + } + } else { + /* + * Let the other processors continue. + */ + kdb_new_cpu = -1; + smp_kdb_wait = 0; + } +#endif + + /* + * Set up debug registers. + */ + kdb_bp_install(); + + kdb_flags &= ~(KDB_FLAG_SUPRESS|KDB_FLAG_FAULT); + + if (diag != KDB_CPUSWITCH) + kdb_active = 0; + + __restore_flags(flags); + + return 0; +} + +/* + * kdb_md + * + * This function implements the 'md' and 'mds' commands. + * + * md|mds [ [ []]] + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_md(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + char fmtchar; + char fmtstr[64]; + int radix, count, width; + unsigned long addr; + unsigned long word; + long offset = 0; + __ksymtab_t symtab; + int diag; + int nextarg; + static unsigned long lastaddr = 0; + static unsigned long lastcount = 0; + static unsigned long lastradix = 0; + char lastbuf[50]; + int symbolic = 0; + + /* + * Defaults in case the relevent environment variables are unset + */ + radix = 16; + count = 8; + width = 4; + + if (argc == 0) { + if (lastaddr == 0) + return KDB_ARGCOUNT; + sprintf(lastbuf, "0x%lx", lastaddr); + argv[1] = lastbuf; + argc = 1; + count = lastcount; + radix = lastradix; + } else { + unsigned long val; + + if (argc >= 2) { + + diag = kdbgetularg(argv[2], &val); + if (!diag) + count = (int) val; + } else { + diag = kdbgetintenv("MDCOUNT", &count); + } + + if (argc >= 3) { + diag = kdbgetularg(argv[3], &val); + if (!diag) + radix = (int) val; + } else { + diag = kdbgetintenv("RADIX",&radix); + } + } + + switch (radix) { + case 10: + fmtchar = 'd'; + break; + case 16: + fmtchar = 'x'; + break; + case 8: + fmtchar = 'o'; + break; + default: + return KDB_BADRADIX; + } + + diag = kdbgetintenv("BYTESPERWORD", &width); + + if (strcmp(argv[0], "mds") == 0) { + symbolic = 1; + width = 4; + } + + switch (width) { + case 4: + sprintf(fmtstr, "%%8.8%c ", fmtchar); + break; + case 2: + sprintf(fmtstr, "%%4.4%c ", fmtchar); + break; + case 1: + sprintf(fmtstr, "%%2.2%c ", fmtchar); + break; + default: + return KDB_BADWIDTH; + } + + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + /* Round address down modulo BYTESPERWORD */ + + addr &= ~(width-1); + + /* + * Remember count and radix for next 'md' + */ + lastcount = count; + lastradix = radix; + + while (count--) { + int num = (symbolic?1 :(16 / width)); + char cbuf[32]; + char *c = cbuf; + char t; + int i; + + for(i=0; i argc) + return KDB_ARGCOUNT; + + diag = kdbgetaddrarg(argc, argv, &nextarg, &contents, NULL, NULL, regs); + if (diag) + return diag; + + if (nextarg != argc + 1) + return KDB_ARGCOUNT; + + /* + * To prevent modification of invalid addresses, check first. + */ + word = kdbgetword(addr, sizeof(word)); + if (kdb_flags & KDB_FLAG_SUPRESS) { + kdb_flags &= ~KDB_FLAG_SUPRESS; + return 0; + } + + *(unsigned long *)(addr) = contents; + + kdb_printf("0x%x = 0x%x\n", addr, contents); + + return 0; +} + +/* + * kdb_go + * + * This function implements the 'go' command. + * + * go [address-expression] + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_go(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + unsigned long addr; + int diag; + int nextarg; + long offset; + + if (argc == 1) { + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + regs->eip = addr; + } else if (argc) + return KDB_ARGCOUNT; + + return KDB_GO; +} + +/* + * kdb_rd + * + * This function implements the 'rd' command. + * + * rd display all general registers. + * rd c display all control registers. + * rd d display all debug registers. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_rd(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + /* + */ + + if (argc == 0) { + return kdbdumpregs(regs, NULL, NULL); + } + + if (argc > 2) { + return KDB_ARGCOUNT; + } + + return kdbdumpregs(regs, argv[1], argv[2]); +} + +/* + * kdb_rm + * + * This function implements the 'rm' (register modify) command. + * + * rm register-name new-contents + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Currently doesn't allow modification of control or + * debug registers, nor does it allow modification + * of model-specific registers (MSR). + */ + +int +kdb_rm(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int diag; + int ind = 0; + unsigned long contents; + + if (argc != 2) { + return KDB_ARGCOUNT; + } + + /* + * Allow presence or absence of leading '%' symbol. + */ + + if (argv[1][0] == '%') + ind = 1; + + diag = kdbgetularg(argv[2], &contents); + if (diag) + return diag; + + diag = kdbsetregcontents(&argv[1][ind], regs, contents); + if (diag) + return diag; + + return 0; +} + +#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) +{ + + return KDB_NOTIMP; +} + +/* + * kdb_reboot + * + * This function implements the 'reboot' command. Reboot the system + * immediately. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Shouldn't return from this function. + */ + +int +kdb_reboot(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + machine_restart(0); + /* NOTREACHED */ + return 0; +} + +/* + * kdb_env + * + * This function implements the 'env' command. Display the current + * environment variables. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_env(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + + for(i=0; i<__nenv; i++) { + if (__env[i]) { + kdb_printf("%s\n", __env[i]); + } + } + + return 0; +} + +/* + * kdb_set + * + * This function implements the 'set' command. Alter an existing + * environment variable or create a new one. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_set(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + char *ep; + size_t varlen, vallen; + + /* + * we can be invoked two ways: + * set var=value argv[1]="var", argv[2]="value" + * set var = value argv[1]="var", argv[2]="=", argv[3]="value" + * - if the latter, shift 'em down. + */ + if (argc == 3) { + argv[2] = argv[3]; + argc--; + } + + if (argc != 2) + return KDB_ARGCOUNT; + + /* + * Tokenizer squashed the '=' sign. argv[1] is variable + * name, argv[2] = value. + */ + varlen = strlen(argv[1]); + vallen = strlen(argv[2]); + ep = kdballocenv(varlen + vallen + 2); + if (ep == (char *)0) + return KDB_ENVBUFFULL; + + sprintf(ep, "%s=%s", argv[1], argv[2]); + + ep[varlen+vallen+1]='\0'; + + for(i=0; i<__nenv; i++) { + if (__env[i] + && ((strncmp(__env[i], argv[1], varlen)==0) + && ((__env[i][varlen] == '\0') + || (__env[i][varlen] == '=')))) { + __env[i] = ep; + return 0; + } + } + + /* + * Wasn't existing variable. Fit into slot. + */ + for(i=0; i<__nenv-1; i++) { + if (__env[i] == (char *)0) { + __env[i] = ep; + return 0; + } + } + + return KDB_ENVFULL; +} + +#if defined(__SMP__) +/* + * kdb_cpu + * + * This function implements the 'cpu' command. + * + * cpu [] + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_cpu(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + unsigned long cpunum; + int diag; + + if (argc == 0) { + int i; + + kdb_printf("Currently on cpu %d\n", smp_processor_id()); + kdb_printf("Available cpus: "); + for (i=0; i NR_CPUS) + || !test_bit(cpunum, &cpu_online_map)) + return KDB_BADCPUNUM; + + kdb_new_cpu = cpunum; + + /* + * Switch to other cpu + */ + return KDB_CPUSWITCH; +} +#endif /* __SMP__ */ + +/* + * kdb_ps + * + * This function implements the 'ps' command which shows + * a list of the active processes. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_ps(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + struct task_struct *p; + + kdb_printf("Task Addr Pid Parent [*] cpu State Tss Command\n"); + for_each_task(p) { + kdb_printf("0x%8.8x %8.8d %8.8d %1.1d %3.3d %s 0x%8.8x%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->tss, + (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 = kdbgetword(addr, sizeof(va)); + if (kdb_flags & KDB_FLAG_SUPRESS) { + kdb_flags &= ~KDB_FLAG_SUPRESS; + return 0; + } + } + + return 0; +} + +/* + * kdb_help + * + * This function implements the 'help' and '?' commands. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +int +kdb_help(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + kdbtab_t *kt; + + kdb_printf("%-15.15s %-20.20s %s\n", "Command", "Usage", "Description"); + kdb_printf("----------------------------------------------------------\n"); + for(kt=kdb_commands; kt->cmd_name; kt++) { + kdb_printf("%-15.15s %-20.20s %s\n", kt->cmd_name, + kt->cmd_usage, kt->cmd_help); + } + return 0; +} + +/* + * kdb_register + * + * This function is used to register a kernel debugger command. + * + * Inputs: + * cmd Command name + * func Function to execute the command + * usage A simple usage string showing arguments + * help A simple help string describing command + * Outputs: + * None. + * Returns: + * zero for success, one if a duplicate command. + * Locking: + * none. + * Remarks: + * + */ + +int +kdb_register(char *cmd, + kdb_func func, + char *usage, + char *help, + short minlen) +{ + int i; + kdbtab_t *kp; + + /* + * Brute force method to determine duplicates + */ + for (i=0, kp=kdb_commands; icmd_name && (strcmp(kp->cmd_name, cmd)==0)) { + kdb_printf("Duplicate kdb command registered: '%s'\n", + cmd); + return 1; + } + } + + /* + * Insert command into first available location in table + */ + for (i=0, kp=kdb_commands; icmd_name == NULL) { + kp->cmd_name = cmd; + kp->cmd_func = func; + kp->cmd_usage = usage; + kp->cmd_help = help; + kp->cmd_flags = 0; + kp->cmd_minlen = minlen; + break; + } + } + return 0; +} + +/* + * kdb_unregister + * + * This function is used to unregister a kernel debugger command. + * It is generally called when a module which implements kdb + * commands is unloaded. + * + * Inputs: + * cmd Command name + * Outputs: + * None. + * Returns: + * zero for success, one command not registered. + * Locking: + * none. + * Remarks: + * + */ + +int +kdb_unregister(char *cmd) +{ + int i; + kdbtab_t *kp; + + /* + * find the command. + */ + for (i=0, kp=kdb_commands; icmd_name && (strcmp(kp->cmd_name, cmd)==0)) { + kp->cmd_name = NULL; + return 0; + } + } + + /* + * Couldn't find it. + */ + return 1; +} + +/* + * kdb_inittab + * + * This function is called by the kdb_init function to initialize + * the kdb command table. It must be called prior to any other + * call to kdb_register. + * + * Inputs: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + */ + +void +kdb_inittab(void) +{ + int i; + kdbtab_t *kp; + + for(i=0, kp=kdb_commands; i < KDB_MAX_COMMANDS; i++,kp++) { + kp->cmd_name = NULL; + } + + kdb_register("md", kdb_md, "", "Display Memory Contents", 1); + kdb_register("mds", kdb_md, "", "Display Memory Symbolically", 0); + kdb_register("mm", kdb_mm, " ", "Modify Memory Contents", 0); + kdb_register("id", kdb_id, "", "Display Instructions", 1); + kdb_register("go", kdb_go, "[]", "Continue Execution", 1); + kdb_register("rd", kdb_rd, "", "Display Registers", 1); + kdb_register("rm", kdb_rm, " ", "Modify Registers", 0); + kdb_register("ef", kdb_ef, "", "Display exception frame", 0); + kdb_register("bt", kdb_bt, "[]", "Stack traceback", 1); + kdb_register("btp", kdb_bt, "", "Display stack for process ", 0); + kdb_register("bp", kdb_bp, "[]", "Set/Display breakpoints", 0); + kdb_register("bl", kdb_bp, "[]", "Display breakpoints", 0); + kdb_register("bpa", kdb_bp, "[]", "Set/Display global breakpoints", 0); + kdb_register("bc", kdb_bc, "", "Clear Breakpoint", 0); + kdb_register("be", kdb_bc, "", "Enable Breakpoint", 0); + kdb_register("bd", kdb_bc, "", "Disable Breakpoint", 0); + kdb_register("ss", kdb_ss, "[<#steps>]", "Single Step", 1); + kdb_register("ssb", kdb_ss, "", "Single step to branch/call", 0); + kdb_register("ll", kdb_ll, " ", "Execute cmd for each element in linked list", 0); + kdb_register("env", kdb_env, "", "Show environment variables", 0); + kdb_register("set", kdb_set, "", "Set environment variables", 0); + kdb_register("help", kdb_help, "", "Display Help Message", 1); + kdb_register("?", kdb_help, "", "Display Help Message", 0); +#if defined(__SMP__) + kdb_register("cpu", kdb_cpu, "","Switch to new cpu", 0); +#endif /* __SMP__ */ + kdb_register("ps", kdb_ps, "", "Display active task list", 0); + kdb_register("reboot", kdb_reboot, "", "Reboot the machine immediately", 0); +#if defined(CONFIG_MAGIC_SYSRQ) + kdb_register("sr", kdb_sr, "", "Magic SysRq key", 0); +#endif +} Index: 2.2.15pre19/arch/i386/kdb/kdb_bp.c --- 2.2.15pre19/arch/i386/kdb/kdb_bp.c Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/arch/i386/kdb/kdb_bp.c Fri, 21 Apr 2000 18:04:41 +1000 kaos (linux-2.2/s/c/47_kdb_bp.c 1.2 644) @@ -0,0 +1,702 @@ +/* + * Kernel Debugger Breakpoint Handler + * + * Copyright 1999, Silicon Graphics, Inc. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + */ + +#include +#include +#include +#if defined(__SMP__) +#include +#include +#endif +#include +#include "kdbsupport.h" + +/* + * Table of breakpoints + */ +#define KDB_MAXBPT 16 +static kdb_bp_t breakpoints[KDB_MAXBPT]; + +static char *rwtypes[] = {"Instruction", "Data Write", "I/O", "Data Access"}; + +/* + * kdb_bp_install + * + * Install breakpoints prior to returning from the kernel debugger. + * This allows the breakpoints to be set upon functions that are + * used internally by kdb, such as printk(). + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + * This function is also called after stopping the other + * processors to re-install breakpoints when they are released + * by kdb. + */ + +void +kdb_bp_install(void) +{ + int i; + + for(i=0; ieip); + if (op1 == 0x0f) + op2 = *(unsigned char *)(ef->eip+1); + if (((op1&0xf0) == 0xe0) /* short disp jumps */ + || ((op1&0xf0) == 0x70) /* Misc. jumps */ + || (op1 == 0xc2) /* ret */ + || (op1 == 0x9a) /* call */ + || ((op1&0xf8) == 0xc8) /* enter, leave, iret, int, */ + || ((op1 == 0x0f) + && ((op2&0xf0)== 0x80))) { + /* + * End the ssb command here. + */ + kdb_flags &= ~(KDB_FLAG_SSB|KDB_FLAG_SS); + } else { + kdb_id1(ef->eip); + rv = 2; /* Indicate ssb - dismiss immediately */ + } + } else { + /* + * Print current insn + */ + kdb_printf("SS trap at 0x%x\n", ef->eip); + kdb_id1(ef->eip); + kdb_flags &= ~KDB_FLAG_SS; + } + + if (rv != 2) + ef->eflags &= ~EF_TF; + } + + if (dr6 & DR6_B0) { + rw = DR7_RW0(dr7); + reg = 0; + goto handle; + } + + if (dr6 & DR6_B1) { + rw = DR7_RW1(dr7); + reg = 1; + goto handle; + } + + if (dr6 & DR6_B2) { + rw = DR7_RW2(dr7); + reg = 2; + goto handle; + } + + if (dr6 & DR6_B3) { + rw = DR7_RW3(dr7); + reg = 3; + goto handle; + } + + if (rv > 0) + goto handled; + + goto unknown; /* dismiss */ + +handle: + /* + * Set Resume Flag + */ + ef->eflags |= EF_RF; + + /* + * Determine which breakpoint was encountered. + */ + for(i=0; ieip); + } + + goto handled; + } + } + +unknown: + ef->eflags |= EF_RF; /* Supress further faults */ + rv = 3; /* Cause kdb() to return */ + +handled: + + /* + * Clear the pending exceptions. + */ + kdb_putdr6(0); + + return rv; +} + +/* + * kdb_printbp + * + * Internal function to format and print a breakpoint entry. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +static void +kdb_printbp(kdb_bp_t *bp, int i) +{ + const char *symname = NULL; + long offset; + __ksymtab_t symtab; + + kdb_printf("%s ", rwtypes[bp->bp_mode]); + + kdb_printf("BP #%d at 0x%x ", + i, bp->bp_addr); + if (kdbnearsym(bp->bp_addr, &symtab)) { + symname = symtab.sym_name; + } + if (symname){ + kdb_printf("(%s", symname); + + offset = bp->bp_addr - symtab.sym_start; + if (offset) { + kdb_printf("+0x%x", offset); + } + kdb_printf(") "); + } + + if (bp->bp_enabled) { + kdb_printf("\n is enabled in dr%d", bp->bp_reg); + if (bp->bp_global) + kdb_printf(" globally"); + else + kdb_printf(" on cpu %d", bp->bp_cpu); + + if (bp->bp_mode != 0) { + kdb_printf(" for %d bytes", bp->bp_length + 1); + } + } else { + kdb_printf("\n is disabled"); + } + + kdb_printf("\n"); +} + +/* + * kdb_bp + * + * Handle the bp, and bpa commands. + * + * [bp|bpa] [DATAR|DATAW|IO [length]] + * + * Parameters: + * argc Count of arguments in argv + * argv Space delimited command line arguments + * envp Environment value + * regs Exception frame at entry to kernel debugger + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic if failure. + * Locking: + * None. + * Remarks: + */ + +int +kdb_bp(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int i; + kdb_bp_t *bp; + int diag; + int free, same; + k_machreg_t addr; + char *symname = NULL; + long offset = 0ul; + int nextarg; + unsigned long mode, length; +#if defined(__SMP__) + int global; +#endif + + if (argc == 0) { + /* + * Display breakpoint table + */ + for(i=0,bp=breakpoints; ibp_free) continue; + + kdb_printbp(bp, i); + } + + return 0; + } + +#if defined(__SMP__) + global = (strcmp(argv[0], "bpa") == 0); +#endif + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, &symname, regs); + if (diag) + return diag; + + mode = 0; /* Default to instruction breakpoint */ + length = 0; /* Length must be zero for insn bp */ + if ((argc + 1) != nextarg) { + if (strnicmp(argv[nextarg], "datar", sizeof("datar")) == 0) { + mode = 3; + } else if (strnicmp(argv[nextarg], "dataw", sizeof("dataw")) == 0) { + mode = 1; + } else if (strnicmp(argv[nextarg], "io", sizeof("io")) == 0) { + mode = 2; + } else { + return KDB_ARGCOUNT; + } + + length = 3; /* Default to 4 byte */ + + nextarg++; + + if ((argc + 1) != nextarg) { + diag = kdbgetularg((char *)argv[nextarg], &length); + if (diag) + return diag; + + if ((length > 4) || (length == 3)) + return KDB_BADLENGTH; + + length--; /* Normalize for debug register */ + nextarg++; + } + + if ((argc + 1) != nextarg) + return KDB_ARGCOUNT; + } + + /* + * Allocate a new bp structure + */ + free = same = KDB_MAXBPT; + for(i=0; ibp_addr = addr; + bp->bp_mode = mode; + bp->bp_length = length; + + /* + * Find a free register + */ + for(i=0; ibp_reg = i; + bp->bp_free = 0; + bp->bp_enabled = 1; +#if defined(__SMP__) + if (global) { + bp->bp_global = 1; + } else { + bp->bp_cpu = smp_processor_id(); + bp->bp_global = 0; + } +#endif + + kdb_printbp(bp, free); + + break; + } + } + /* + * XXX if not enough regs, fail. Eventually replace + * first byte of instruction with int03. + */ + if (i==KDB_DBREGS) + return KDB_TOOMANYDBREGS; + + return 0; +} + +/* + * kdb_bc + * + * Handles the 'bc', 'be', and 'bd' commands + * + * [bd|bc|be] + * + * Parameters: + * argc Count of arguments in argv + * argv Space delimited command line arguments + * envp Environment value + * regs Exception frame at entry to kernel debugger + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + */ +#define KDBCMD_BC 0 +#define KDBCMD_BE 1 +#define KDBCMD_BD 2 + +int +kdb_bc(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + k_machreg_t addr; + kdb_bp_t *bp = 0; + int lowbp = KDB_MAXBPT; + int highbp = 0; + int done = 0; + int i; + int diag; + int cmd; /* KDBCMD_B? */ + + if (strcmp(argv[0], "be") == 0) { + cmd = KDBCMD_BE; + } else if (strcmp(argv[0], "bd") == 0) { + cmd = KDBCMD_BD; + } else + cmd = KDBCMD_BC; + + if (argc != 1) + return KDB_ARGCOUNT; + + if (strcmp(argv[1], "*") == 0) { + lowbp = 0; + highbp = KDB_MAXBPT; + } else { + diag = kdbgetularg(argv[1], &addr); + if (diag) + return diag; + + /* + * For addresses less than the maximum breakpoint number, + * assume that the breakpoint number is desired. + */ + if (addr < KDB_MAXBPT) { + bp = &breakpoints[addr]; + lowbp = highbp = addr; + highbp++; + } else { + for(i=0; ibp_free) + continue; + + done++; + + switch (cmd) { + case KDBCMD_BC: + kdbremovedbreg(bp->bp_reg); + dbregs[bp->bp_reg] = 0xffffffff; + kdb_printf("Breakpoint %d at 0x%x in dr%d cleared\n", + i, bp->bp_addr, bp->bp_reg); + bp->bp_free = 1; + bp->bp_addr = 0; + bp->bp_enabled = 0; + bp->bp_global = 0; + break; + case KDBCMD_BE: + { + int r; + + for(r=0; rbp_reg = r; + bp->bp_enabled = 1; + kdb_printf("Breakpoint %d at 0x%x in dr%d enabled\n", + i, bp->bp_addr, bp->bp_reg); + break; + } + } + if (r == KDB_DBREGS) + return KDB_TOOMANYDBREGS; + + break; + } + case KDBCMD_BD: + kdbremovedbreg(bp->bp_reg); + dbregs[bp->bp_reg] = 0xffffffff; + bp->bp_enabled = 0; + kdb_printf("Breakpoint %d at 0x%x in dr%d disabled\n", + i, bp->bp_addr, bp->bp_reg); + break; + } + } + + return (!done)?KDB_BPTNOTFOUND:0; +} + +/* + * kdb_initbptab + * + * Initialize the breakpoint table. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdb_initbptab(void) +{ + int i; + + /* + * First time initialization. + */ + for (i=0; i] + * ssb + * + * Parameters: + * argc Argument count + * argv Argument vector + * envp Environment vector + * regs Registers at time of entry to kernel debugger + * Outputs: + * None. + * Returns: + * 0 for success, a kdb error if failure. + * Locking: + * None. + * Remarks: + * + * Set the trace flag in the EFLAGS register to trigger + * a debug trap after the next instruction. Print the + * current instruction. + * + * For 'ssb', set the trace flag in the debug trap handler + * after printing the current insn and return directly without + * invoking the kdb command processor, until a branch instruction + * is encountered or SSCOUNT lines are printed. + */ + +int +kdb_ss(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int ssb = 0; + + ssb = (strcmp(argv[0], "ssb") == 0); + if ((ssb && (argc != 0)) + || (!ssb && (argc > 1))) { + return KDB_ARGCOUNT; + } + +#if 0 + /* + * Fetch provided count + */ + diag = kdbgetularg(argv[1], &sscount); + if (diag) + return diag; +#endif + + /* + * Print current insn + */ + kdb_id1(regs->eip); + + /* + * Set trace flag and go. + */ + regs->eflags |= EF_TF; + + kdb_flags |= KDB_FLAG_SS; + + if (ssb) + kdb_flags |= KDB_FLAG_SSB; + + return KDB_GO; +} Index: 2.2.15pre19/arch/i386/kdb/kdb_bt.c --- 2.2.15pre19/arch/i386/kdb/kdb_bt.c Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/arch/i386/kdb/kdb_bt.c Fri, 21 Apr 2000 21:58:56 +1000 kaos (linux-2.2/s/c/46_kdb_bt.c 1.3 644) @@ -0,0 +1,408 @@ + /* + * Minimalist Kernel Debugger + * + * Machine dependent stack traceback code. + * + * Copyright 1999, Silicon Graphics, Inc. + * + * Written by Scott Lurndal at Silicon Graphics, March 1999. + */ + +#include +#include +#include +#include +#include +#include +#include "kdbsupport.h" + + +#if defined(CONFIG_KDB_FRAMEPTR) +/* + * kdb_prologue + * + * This function analyzes a gcc-generated function prototype + * when frame pointers are enabled to determine the amount of + * automatic storage and register save storage is used on the + * stack of the target function. + * Parameters: + * eip Address of function to analyze + * Outputs: + * *nauto # bytes of automatic storage + * *nsave # of saved registers + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + * A prologue generally looks like: + * + * pushl %ebp (All functions) + * movl %esp, %ebp (All functions) + * subl $auto, %esp [some functions] + * pushl reg [some functions] + * pushl reg [some functions] + */ + +void +kdb_prologue(unsigned long eip, unsigned long *nauto, unsigned long *nsave) +{ + size_t insn_size = sizeof(unsigned char); + + *nauto = 0; + *nsave = 0; + + if (eip == 0) + return; + + if ((kdbgetword(eip, insn_size) != 0x55) /* pushl %ebp */ + || (kdbgetword(eip+1, insn_size) != 0x89)){/* movl esp, ebp */ + /* + * Unknown prologue type. + */ + return; + } + + if (kdbgetword(eip+3, insn_size) == 0x83) { + *nauto = kdbgetword(eip+5, sizeof(unsigned char)); + eip += 6; + } else if (kdbgetword(eip+3, insn_size) == 0x81) { + *nauto = kdbgetword(eip+5, sizeof(unsigned long)); + eip += 9; + } else + eip += 3; + + + while ((kdbgetword(eip, insn_size)&0xf8) == 0x50) + (*nsave)++, eip++; +} +#endif + +/* + * kdb_bt + * + * This function implements the 'bt' command. Print a stack + * traceback. + * + * bt [] (addr-exp is for alternate stacks) + * btp (Kernel stack for ) + * + * address expression refers to a return address on the stack. It + * is expected to be preceeded by a frame pointer. + * + * Inputs: + * argc argument count + * argv argument vector + * envp environment vector + * regs registers at time kdb was entered. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Doing this without a frame pointer is _hard_. some simple + * things are done here, but we usually don't find more than the + * first couple of frames yet. More coming in this area. + * + * mds comes in handy when examining the stack to do a manual + * traceback. + */ + +int +kdb_bt(int argc, const char **argv, const char **envp, struct pt_regs *regs) +{ + int done = 0; +#if !defined(CONFIG_KDB_FRAMEPTR) + unsigned long sp; +#endif + unsigned long base, limit, esp, ebp, eip; + unsigned long addr; + long offset = 0; + int nextarg; + int argcount=5; + int found, nfound; + int diag; + struct pt_regs taskregs; + struct frame { + unsigned long ebp; + unsigned long eip; + } old; + unsigned long stackbase = (unsigned long)current; + __ksymtab_t symtab, nsymtab; + + /* + * Determine how many possible arguments to print. + */ + diag = kdbgetintenv("BTARGS", &argcount); + + if (strcmp(argv[0], "btp") == 0){ + struct task_struct *p; + int pid; + + diag = kdbgetularg((char *)argv[1], (unsigned long*)&pid); + if (diag) + return diag; + + taskregs.eax = 1; + for_each_task(p) { + if (p->pid == pid) { + taskregs.eip = p->tss.eip; + taskregs.esp = p->tss.esp; + taskregs.ebp = p->tss.ebp; + /* + * Since we don't really use the TSS + * to store register between task switches, + * attempt to locate real ebp (should be + * top of stack if task is in schedule) + */ + if (taskregs.ebp == 0) { + taskregs.ebp = + *(unsigned long *)(taskregs.esp); + } + + taskregs.eax = 0; + stackbase = (unsigned long)p; + break; + } + } + + if (taskregs.eax == 1) { + kdb_printf("No process with pid == %d found\n", + pid); + return 0; + } + regs = &taskregs; + } else { + if (argc) { + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs); + if (diag) + return diag; + + /* + * We assume the supplied address is points to an + * EIP value on the stack, and the word pushed above + * it is an EBP value. Start as if we were in the + * middle of that frame. + */ + taskregs.eip = kdbgetword(addr, sizeof(unsigned long)); + taskregs.ebp = kdbgetword(addr-4, sizeof(unsigned long)); + taskregs.ebp = kdbgetword(taskregs.ebp, sizeof(unsigned long)); + regs = &taskregs; + } + } + + + found = kdbnearsym(regs->eip, &symtab); + if (!found) { + kdb_printf("Cannot determine function for eip = 0x%x\n", + regs->eip); + return 0; + } +#if !defined(CONFIG_KDB_FRAMEPTR) + /* + * starting with %esp value, track the stack back to + * the end. + */ + + sp = (unsigned long) (1+regs); /* Point past exception frame */ + sp -= 8; /* Adjust to encompass return addr */ + if (regs->xcs & 3) { + /* User space? */ + sp = regs->esp; + } + sp -= 8; /* Adjust to encompass return addr */ + base = sp & ~0x1fff; /* XXX - use stack size constant */ + + + kdb_printf(" ESP EIP Function(args)\n"); + kdb_printf("0x%x 0x%x %s()\n", + sp, regs->eip, + symtab.sym_name); + kdb_printf(" %s %s 0x%x 0x%x 0x%x\n", + symtab.mod_name, + symtab.sec_name, + symtab.sec_start, + symtab.sym_start, + symtab.sym_end); + + while (!done) { + unsigned long word; + + word = *(unsigned long *)(sp); + if (kdbnearsym(word, &symtab)) { + /* word is inside a kernel or module section */ + +#if defined(NOTNOW) + kdb_printf("word %x sp %x char %x off %x\n", + word, sp, *(unsigned char *)(word - 5), + *(signed long *)(word - 4)); +#endif + /* + * If the instruction 5 bytes before the + * return instruction is a call, treat this + * entry as a probable activation record + */ + if (*(unsigned char *)(word - 5) == 0xe8) { + signed long val = *(signed long *)(word - 4); + int i; + + /* + * Is this a real activation record? + */ + /* + * Of course, this misses all the + * indirect calls, etc. XXX XXX + */ + if ((val + word) == symtab.sym_start) { + kdb_printf("0x%x 0x%x", + sp, word); + found = kdbnearsym(word, &symtab); + if (found) { + kdb_printf(" %s(", symtab.sym_name); + for(i=0; i= base+0x2000) /* XXX - use stack size constant */ + done = 1; + } +#else + kdb_printf(" EBP EIP Function(args)\n"); + + base = stackbase; + + esp = regs->esp; + ebp = regs->ebp; + eip = regs->eip; + + limit = base + 0x2000; /* stack limit */ + +#if 0 + printk("stackbase = 0x%lx base = 0x%lx limit = 0x%lx ebp = 0x%lx esp = 0x%lx eip = 0x%lx\n", + stackbase, base, limit, ebp, esp, eip); +#endif + + if ((eip == symtab.sym_start) /* Beginning of function */ + || (eip == symtab.sym_start+1)) { /* Right after pushl %ebp */ + /* + * If at start of function, create a dummy frame + */ + old.ebp = ebp; + if (kdb_flags & KDB_FLAG_FAULT) { + if ((regs->xcs&0xffff) == 0x10) { + /* + * For instruction breakpoint on a target of a + * call instruction, we must fetch the caller's + * eip shall we say, differently. + */ + old.eip = regs->esp; /* Don't Ask */ + } else { + old.eip = *(unsigned long *)(regs+1); + } + } else { + old.eip = (eip == symtab.sym_start) + ?kdbgetword(esp, sizeof(unsigned long)) + :kdbgetword(esp+4, sizeof(unsigned long)); + } + ebp = (unsigned long)&old; + } + + /* + * While the ebp remains within the stack, continue to + * print stack entries. + * + * XXX - this code depends on the fact that kernel + * stacks are aligned on address boundaries + * congruent to 0 modulo 8192 and are 8192 bytes + * in length and are part of the 8k bytes based + * at the process structure (e.g. current). + * + */ + + while (!done) { + unsigned long nebp, neip; + + kdb_printf("0x%x 0x%x ", ebp, eip); + if (found) + kdb_printf("%s", symtab.sym_name); + else + kdb_printf("?"); + + if (ebp < PAGE_OFFSET) { + done++; + kdb_printf("\n"); + continue; + } + + nebp = kdbgetword(ebp, sizeof(unsigned long)); + neip = kdbgetword(ebp+4, sizeof(unsigned long)); + + if (found && eip != symtab.sym_start) { + kdb_printf("+0x%x", eip - symtab.sym_start); + } + + /* + * Get frame for caller to determine the frame size + * and argument count. + */ + nfound = kdbnearsym(neip, &nsymtab); + if (nfound) { + int i; + unsigned long nauto, nsave, nargs; + + kdb_printf("("); + + kdb_prologue(nsymtab.sym_start, &nauto, &nsave); + + nargs = (nebp - ebp - 8 - nauto - (nsave * 4))/4; + if (nargs > argcount) + nargs = argcount; + for(i=0; i +#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 +} + +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++; + printk("%c %c", 0x08, 0x08); + } + continue; + } + serial_out(kdb_port, UART_TX, ch); + if (ch == 13) { /* CR */ + *cp++ = '\n'; + *cp++ = '\0'; + serial_out(kdb_port, UART_TX, 10); + return(buffer); + } + /* + * Discard excess characters + */ + if (bufsize > 0) { + *cp++ = ch; + bufsize--; + } + } + while (((status = serial_inp(kdb_port, UART_LSR)) + & UART_LSR_DR) == 0); + } + } + + while (1) { + + /* + * Wait for a valid scancode + */ + + while ((inb(KBD_STATUS_REG) & KBD_STAT_OBF) == 0) + ; + + /* + * Fetch the scancode + */ + scancode = inb(KBD_DATA_REG); + scanstatus = inb(KBD_STATUS_REG); + + /* + * Ignore mouse events. + */ + if (scanstatus & KBD_STAT_MOUSE_OBF) + continue; + + /* + * Ignore release, trigger on make + * (except for shift keys, where we want to + * keep the shift state so long as the key is + * held down). + */ + + if (((scancode&0x7f) == 0x2a) + || ((scancode&0x7f) == 0x36)) { + /* + * Next key may use shift table + */ + if ((scancode & 0x80) == 0) { + shift_key=1; + } else { + shift_key=0; + } + continue; + } + + if ((scancode&0x7f) == 0x1d) { + /* + * Left ctrl key + */ + if ((scancode & 0x80) == 0) { + ctrl_key = 1; + } else { + ctrl_key = 0; + } + continue; + } + + if ((scancode & 0x80) != 0) + continue; + + scancode &= 0x7f; + + /* + * Translate scancode + */ + + if (scancode == 0x3a) { + /* + * Toggle caps lock + */ + shift_lock ^= 1; + leds ^= 0x4; /* toggle caps lock led */ + + kdb_kbdsetled(leds); + continue; + } + + if (scancode == 0x0e) { + /* + * Backspace + */ + if (cp > buffer) { + --cp, bufsize++; + + /* + * XXX - erase character on screen + */ + printk("%c %c", 0x08, 0x08); + } + continue; + } + + if (scancode == 0xe0) { + continue; + } + + /* + * For Japanese 86/106 keyboards + * See comment in drivers/char/pc_keyb.c. + * - Masahiro Adegawa + */ + if (scancode == 0x73) { + scancode = 0x59; + } else if (scancode == 0x7d) { + scancode = 0x7c; + } + + if (!shift_lock && !shift_key) { + keychar = plain_map[scancode]; + } else if (shift_lock || shift_key) { + keychar = shift_map[scancode]; + } else if (ctrl_key) { + keychar = ctrl_map[scancode]; + } else { + keychar = 0x0020; + printk("Unknown state/scancode (%d)\n", scancode); + } + + if ((scancode & 0x7f) == 0x1c) { + /* + * enter key. All done. 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? + */ + printk("kdb: expected enter got 0x%x status 0x%x\n", + scancode, scanstatus); + } + + printk("\n"); + break; + } + + /* + * echo the character. + */ + printk("%c", keychar&0xff); + + if (bufsize) { + --bufsize; + *cp++ = keychar&0xff; + } else { + printk("buffer overflow\n"); + break; + } + + } + + *cp++ = '\n'; /* White space for parser */ + *cp++ = '\0'; /* String termination */ + +#if defined(NOTNOW) + cp = buffer; + while (*cp) { + printk("char 0x%x\n", *cp++); + } +#endif + + return buffer; +} Index: 2.2.15pre19/arch/i386/kdb/kdb_io.h --- 2.2.15pre19/arch/i386/kdb/kdb_io.h Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/arch/i386/kdb/kdb_io.h Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/s/c/44_kdb_io.h 1.1 644) @@ -0,0 +1,15 @@ +/* + * Kernel Debugger Breakpoint Handler + * + * Copyright 1999, Silicon Graphics, Inc. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + */ + +#if !defined(_KDB_IO_H) +#define _KDB_IO_H + +extern void kdb_resetkeyboard(void); +char * kdb_getscancode(char *, size_t); + +#endif /* _KDB_IO_H */ Index: 2.2.15pre19/arch/i386/kdb/kdbsupport.c --- 2.2.15pre19/arch/i386/kdb/kdbsupport.c Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/arch/i386/kdb/kdbsupport.c Fri, 21 Apr 2000 18:04:41 +1000 kaos (linux-2.2/s/c/43_kdbsupport 1.2 644) @@ -0,0 +1,980 @@ +/* + * Kernel Debugger Breakpoint Handler + * + * Copyright 1999, Silicon Graphics, Inc. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Masahiro Adegawa 1999/12/01 + * Enhance module symbol handling. + * Keith Owens 2000/04/20 + * Use kallsyms for all symbol handling. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kdbsupport.h" +#include "kdb_io.h" + +k_machreg_t dbregs[KDB_DBREGS]; + +void +kdb_init(void) +{ + extern void kdb_inittab(void); + + kdb_inittab(); + kdb_initbptab(); + kdb_disinit(); + kdb_printf("kdb version %d.%d by Scott Lurndal. "\ + "Copyright SGI, All Rights Reserved\n", + KDB_MAJOR_VERSION, KDB_MINOR_VERSION); +} + +/* + * kdbprintf + * kdbgetword + * kdb_getstr + */ + +char * +kbd_getstr(char *buffer, size_t bufsize, char *prompt) +{ +#if defined(CONFIG_SMP) + kdb_printf(prompt, smp_processor_id()); +#else + kdb_printf("%s", prompt); +#endif + + return kdb_getscancode(buffer, bufsize); + +} + +int +kdb_printf(const char *fmt, ...) +{ + char buffer[256]; + va_list ap; + int diag; + int linecount; + + diag = kdbgetintenv("LINES", &linecount); + if (diag) + linecount = 22; + + va_start(ap, fmt); + vsprintf(buffer, fmt, ap); + va_end(ap); + + printk("%s", buffer); + + if (strchr(buffer, '\n') != NULL) { + kdb_nextline++; + } + + if (kdb_nextline == linecount) { + char buf1[16]; +#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 + + printk(moreprompt); + (void) kdb_getscancode(buf1, sizeof(buf1)); + + kdb_nextline = 1; + + if ((buf1[0] == 'q') + || (buf1[0] == 'Q')) { + kdb_longjmp(&kdbjmpbuf[smp_processor_id()], 1); + } + } + + return 0; +} + +unsigned long +kdbgetword(unsigned long addr, int width) +{ + /* + * This function checks the address for validity. Any address + * in the range PAGE_OFFSET to high_memory is legal, any address + * which maps to a vmalloc region is legal, and any address which + * is a user address, we use get_user() to verify validity. + */ + + if (addr < PAGE_OFFSET) { + /* + * Usermode address. + */ + unsigned long diag; + unsigned long ulval; + + switch (width) { + case 4: + { unsigned long *lp; + + lp = (unsigned long *) addr; + diag = get_user(ulval, lp); + break; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *) addr; + diag = get_user(ulval, sp); + break; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *) addr; + diag = get_user(ulval, cp); + break; + } + default: + printk("kdbgetword: Bad width\n"); + return 0L; + } + + if (diag) { + if ((kdb_flags & KDB_FLAG_SUPRESS) == 0) { + printk("kdb: Bad user address 0x%lx\n", addr); + kdb_flags |= KDB_FLAG_SUPRESS; + } + return 0L; + } + kdb_flags &= ~KDB_FLAG_SUPRESS; + return ulval; + } + + if (addr > (unsigned long)high_memory) { + extern int kdb_vmlist_check(unsigned long, unsigned long); + + if (!kdb_vmlist_check(addr, addr+width)) { + /* + * Would appear to be an illegal kernel address; + * Print a message once, and don't print again until + * a legal address is used. + */ + if ((kdb_flags & KDB_FLAG_SUPRESS) == 0) { + printk("kdb: Bad kernel address 0x%lx\n", addr); + kdb_flags |= KDB_FLAG_SUPRESS; + } + return 0L; + } + } + + /* + * A good address. Reset error flag. + */ + kdb_flags &= ~KDB_FLAG_SUPRESS; + + switch (width) { + case 4: + { unsigned long *lp; + + lp = (unsigned long *)(addr); + return *lp; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *)(addr); + return *sp; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *)(addr); + return *cp; + } + } + + printk("kdbgetword: Bad width\n"); + return 0L; +} + +int +kdbinstalltrap(int type, handler_t newh, handler_t *oldh) +{ + /* + * Usurp INTn. XXX - TBD. + */ + + return 0; +} + +int +kdbinstalldbreg(kdb_bp_t *bp) +{ + k_machreg_t dr7; + + dr7 = kdb_getdr7(); + + kdb_putdr(bp->bp_reg, bp->bp_addr); + + dr7 |= DR7_GE; + + switch (bp->bp_reg){ + case 0: + DR7_RW0SET(dr7,bp->bp_mode); + DR7_LEN0SET(dr7,bp->bp_length); + DR7_G0SET(dr7); + break; + case 1: + DR7_RW1SET(dr7,bp->bp_mode); + DR7_LEN1SET(dr7,bp->bp_length); + DR7_G1SET(dr7); + break; + case 2: + DR7_RW2SET(dr7,bp->bp_mode); + DR7_LEN2SET(dr7,bp->bp_length); + DR7_G2SET(dr7); + break; + case 3: + DR7_RW3SET(dr7,bp->bp_mode); + DR7_LEN3SET(dr7,bp->bp_length); + DR7_G3SET(dr7); + break; + default: + kdb_printf("Bad debug register!! %d\n", bp->bp_reg); + break; + } + + kdb_putdr7(dr7); + return 0; +} + +void +kdbremovedbreg(int regnum) +{ + k_machreg_t dr7; + + dr7 = kdb_getdr7(); + + kdb_putdr(regnum, 0); + + switch (regnum) { + case 0: + DR7_G0CLR(dr7); + DR7_L0CLR(dr7); + break; + case 1: + DR7_G1CLR(dr7); + DR7_L1CLR(dr7); + break; + case 2: + DR7_G2CLR(dr7); + DR7_L2CLR(dr7); + break; + case 3: + DR7_G3CLR(dr7); + DR7_L3CLR(dr7); + break; + default: + kdb_printf("Bad debug register!! %d\n", regnum); + break; + } + + kdb_putdr7(dr7); +} + +k_machreg_t +kdb_getdr6(void) +{ + return kdb_getdr(6); +} + +k_machreg_t +kdb_getdr7(void) +{ + return kdb_getdr(7); +} + +k_machreg_t +kdb_getdr(int regnum) +{ + k_machreg_t contents = 0; +#if defined(__GNUC__) + switch(regnum) { + case 0: + __asm__ ("movl %%db0,%0\n\t":"=r"(contents)); + break; + case 1: + __asm__ ("movl %%db1,%0\n\t":"=r"(contents)); + break; + case 2: + __asm__ ("movl %%db2,%0\n\t":"=r"(contents)); + break; + case 3: + __asm__ ("movl %%db3,%0\n\t":"=r"(contents)); + break; + case 4: + case 5: + break; + case 6: + __asm__ ("movl %%db6,%0\n\t":"=r"(contents)); + break; + case 7: + __asm__ ("movl %%db7,%0\n\t":"=r"(contents)); + break; + default: + break; + } + +#endif /* __GNUC__ */ + return contents; +} + + +k_machreg_t +kdb_getcr(int regnum) +{ + k_machreg_t contents = 0; +#if defined(__GNUC__) + switch(regnum) { + case 0: + __asm__ ("movl %%cr0,%0\n\t":"=r"(contents)); + break; + case 1: + break; + case 2: + __asm__ ("movl %%cr2,%0\n\t":"=r"(contents)); + break; + case 3: + __asm__ ("movl %%cr3,%0\n\t":"=r"(contents)); + break; + case 4: + __asm__ ("movl %%cr4,%0\n\t":"=r"(contents)); + break; + default: + break; + } + +#endif /* __GNUC__ */ + return contents; +} + +void +kdb_putdr6(k_machreg_t contents) +{ + kdb_putdr(6, contents); +} + +void +kdb_putdr7(k_machreg_t contents) +{ + kdb_putdr(7, contents); +} + +void +kdb_putdr(int regnum, k_machreg_t contents) +{ +#if defined(__GNUC__) + switch(regnum) { + case 0: + __asm__ ("movl %0,%%db0\n\t"::"r"(contents)); + break; + case 1: + __asm__ ("movl %0,%%db1\n\t"::"r"(contents)); + break; + case 2: + __asm__ ("movl %0,%%db2\n\t"::"r"(contents)); + break; + case 3: + __asm__ ("movl %0,%%db3\n\t"::"r"(contents)); + break; + case 4: + case 5: + break; + case 6: + __asm__ ("movl %0,%%db6\n\t"::"r"(contents)); + break; + case 7: + __asm__ ("movl %0,%%db7\n\t"::"r"(contents)); + break; + default: + break; + } +#endif +} + +/* + * Symbol table functions. + */ + +/* + * kdbgetsymval + * + * Return the address of the given symbol. + * + * Parameters: + * symname Character string containing symbol name + * symtab Structure to receive results + * Outputs: + * Returns: + * 0 Symbol name not found + * 1 symname found, data in symtab + * Locking: + * None. + * Remarks: + */ + +int +kdbgetsymval(const char *symname, __ksymtab_t *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 contains this address + * 1 Address mapped to module/symbol/section, data in symtab + * Locking: + * None. + * Remarks: + */ + +int +kdbnearsym(unsigned long addr, __ksymtab_t *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)); +} + +/* + * kdbgetregcontents + * + * Return the contents of the register specified by the + * input string argument. Return an error if the string + * does not match a machine register. + * + * The following pseudo register names are supported: + * ®s - Prints address of exception frame + * kesp - Prints kernel stack pointer at time of fault + * % - Uses the value of the registers at the + * last time the user process entered kernel + * mode, instead of the registers at the time + * kdb was entered. + * + * Parameters: + * regname Pointer to string naming register + * regs Pointer to structure containing registers. + * Outputs: + * *contents Pointer to unsigned long to recieve register contents + * Returns: + * 0 Success + * KDB_BADREG Invalid register name + * Locking: + * None. + * Remarks: + * + * Note that this function is really machine independent. The kdb + * register list is not, however. + */ + +static struct kdbregs { + char *reg_name; + size_t reg_offset; +} kdbreglist[] = { + { "eax", offsetof(struct pt_regs, eax) }, + { "ebx", offsetof(struct pt_regs, ebx) }, + { "ecx", offsetof(struct pt_regs, ecx) }, + { "edx", offsetof(struct pt_regs, edx) }, + + { "esi", offsetof(struct pt_regs, esi) }, + { "edi", offsetof(struct pt_regs, edi) }, + { "esp", offsetof(struct pt_regs, esp) }, + { "eip", offsetof(struct pt_regs, eip) }, + + { "ebp", offsetof(struct pt_regs, ebp) }, + { " ss", offsetof(struct pt_regs, xss) }, + { " cs", offsetof(struct pt_regs, xcs) }, + { "eflags", offsetof(struct pt_regs, eflags) }, + + { " ds", offsetof(struct pt_regs, xds) }, + { " es", offsetof(struct pt_regs, xes) }, + { "origeax", offsetof(struct pt_regs, orig_eax) }, + +}; + +static const int nkdbreglist = sizeof(kdbreglist) / sizeof(struct kdbregs); + +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 +kdbgetregcontents(const char *regname, + struct pt_regs *regs, + unsigned long *contents) +{ + int i; + + if (strcmp(regname, "®s") == 0) { + *contents = (unsigned long)regs; + return 0; + } + + if (strcmp(regname, "kesp") == 0) { + *contents = (unsigned long)regs + sizeof(struct pt_regs); + return 0; + } + + if (regname[0] == '%') { + /* User registers: %%e[a-c]x, etc */ + regname++; + regs = (struct pt_regs *) + (current->tss.esp0 - sizeof(struct pt_regs)); + } + + for (i=0; i + * + * Parameters: + * regname Pointer to string naming register + * regs Pointer to structure containing registers. + * contents Unsigned long containing new register contents + * Outputs: + * Returns: + * 0 Success + * KDB_BADREG Invalid register name + * Locking: + * None. + * Remarks: + */ + +int +kdbsetregcontents(const char *regname, + struct pt_regs *regs, + unsigned long contents) +{ + int i; + + if (regname[0] == '%') { + regname++; + regs = (struct pt_regs *) + (current->tss.esp0 - sizeof(struct pt_regs)); + } + + for (i=0; itss.esp0 - sizeof(struct pt_regs)); + } + + if (type == NULL) { + for (i=0; i + + /* + * This file provides definitions for functions that + * are dependent upon the product into which kdb is + * linked. + * + * This version is for linux. + */ +typedef void (*handler_t)(struct pt_regs *); +typedef unsigned long k_machreg_t; + +extern char* kbd_getstr(char *, size_t, char *); +extern int kdbinstalltrap(int, handler_t, handler_t*); +extern int kdbinstalldbreg(kdb_bp_t*); +extern void kdbremovedbreg(int); +extern void kdb_initbptab(void); +extern int kdbgetregcontents(const char *, struct pt_regs *, unsigned long *); +extern int kdbsetregcontents(const char *, struct pt_regs *, unsigned long); +extern int kdbdumpregs(struct pt_regs *, const char *, const char *); + + +/* + * kdb_db_trap is a processor dependent routine invoked + * from kdb() via the #db trap handler. It handles breakpoints involving + * the processor debug registers and handles single step traps + * using the processor trace flag. + */ + +#define KDB_DB_BPT 0 /* Straight breakpoint */ +#define KDB_DB_SS 1 /* Single Step trap */ +#define KDB_DB_SSB 2 /* Single Step, caller should continue */ + +extern int kdb_db_trap(struct pt_regs *); + + + /* + * Support for ia32 architecture debug registers. + */ +#define KDB_DBREGS 4 +extern k_machreg_t dbregs[]; + +#define DR6_BT 0x00008000 +#define DR6_BS 0x00004000 +#define DR6_BD 0x00002000 + +#define DR6_B3 0x00000008 +#define DR6_B2 0x00000004 +#define DR6_B1 0x00000002 +#define DR6_B0 0x00000001 + +#define DR7_RW_VAL(dr, drnum) \ + (((dr) >> (16 + (4 * (drnum)))) & 0x3) + +#define DR7_RW_SET(dr, drnum, rw) \ + do { \ + (dr) &= ~(0x3 << (16 + (4 * (drnum)))); \ + (dr) |= (((rw) & 0x3) << (16 + (4 * (drnum)))); \ + } while (0) + +#define DR7_RW0(dr) DR7_RW_VAL(dr, 0) +#define DR7_RW0SET(dr,rw) DR7_RW_SET(dr, 0, rw) +#define DR7_RW1(dr) DR7_RW_VAL(dr, 1) +#define DR7_RW1SET(dr,rw) DR7_RW_SET(dr, 1, rw) +#define DR7_RW2(dr) DR7_RW_VAL(dr, 2) +#define DR7_RW2SET(dr,rw) DR7_RW_SET(dr, 2, rw) +#define DR7_RW3(dr) DR7_RW_VAL(dr, 3) +#define DR7_RW3SET(dr,rw) DR7_RW_SET(dr, 3, rw) + + +#define DR7_LEN_VAL(dr, drnum) \ + (((dr) >> (18 + (4 * (drnum)))) & 0x3) + +#define DR7_LEN_SET(dr, drnum, rw) \ + do { \ + (dr) &= ~(0x3 << (18 + (4 * (drnum)))); \ + (dr) |= (((rw) & 0x3) << (18 + (4 * (drnum)))); \ + } while (0) + +#define DR7_LEN0(dr) DR7_LEN_VAL(dr, 0) +#define DR7_LEN0SET(dr,len) DR7_LEN_SET(dr, 0, len) +#define DR7_LEN1(dr) DR7_LEN_VAL(dr, 1) +#define DR7_LEN1SET(dr,len) DR7_LEN_SET(dr, 1, len) +#define DR7_LEN2(dr) DR7_LEN_VAL(dr, 2) +#define DR7_LEN2SET(dr,len) DR7_LEN_SET(dr, 2, len) +#define DR7_LEN3(dr) DR7_LEN_VAL(dr, 3) +#define DR7_LEN3SET(dr,len) DR7_LEN_SET(dr, 3, len) + +#define DR7_G0(dr) (((dr)>>1)&0x1) +#define DR7_G0SET(dr) ((dr) |= 0x2) +#define DR7_G0CLR(dr) ((dr) &= ~0x2) +#define DR7_G1(dr) (((dr)>>3)&0x1) +#define DR7_G1SET(dr) ((dr) |= 0x8) +#define DR7_G1CLR(dr) ((dr) &= ~0x8) +#define DR7_G2(dr) (((dr)>>5)&0x1) +#define DR7_G2SET(dr) ((dr) |= 0x20) +#define DR7_G2CLR(dr) ((dr) &= ~0x20) +#define DR7_G3(dr) (((dr)>>7)&0x1) +#define DR7_G3SET(dr) ((dr) |= 0x80) +#define DR7_G3CLR(dr) ((dr) &= ~0x80) + +#define DR7_L0(dr) (((dr))&0x1) +#define DR7_L0SET(dr) ((dr) |= 0x1) +#define DR7_L0CLR(dr) ((dr) &= ~0x1) +#define DR7_L1(dr) (((dr)>>2)&0x1) +#define DR7_L1SET(dr) ((dr) |= 0x4) +#define DR7_L1CLR(dr) ((dr) &= ~0x4) +#define DR7_L2(dr) (((dr)>>4)&0x1) +#define DR7_L2SET(dr) ((dr) |= 0x10) +#define DR7_L2CLR(dr) ((dr) &= ~0x10) +#define DR7_L3(dr) (((dr)>>6)&0x1) +#define DR7_L3SET(dr) ((dr) |= 0x40) +#define DR7_L3CLR(dr) ((dr) &= ~0x40) + +#define DR7_GD 0x00002000 /* General Detect Enable */ +#define DR7_GE 0x00000200 /* Global exact */ +#define DR7_LE 0x00000100 /* Local exact */ + +extern k_machreg_t kdb_getdr6(void); +extern void kdb_putdr6(k_machreg_t); + +extern k_machreg_t kdb_getdr7(void); +extern void kdb_putdr7(k_machreg_t); + +extern k_machreg_t kdb_getdr(int); +extern void kdb_putdr(int, k_machreg_t); + +extern k_machreg_t kdb_getcr(int); + + /* + * Processor breakpoint state save & restore functions. + */ +extern void kdb_bp_install(void); +extern void kdb_bp_remove(void); + +/* + * Support for setjmp/longjmp + */ +#define JB_BX 0 +#define JB_SI 1 +#define JB_DI 2 +#define JB_BP 3 +#define JB_SP 4 +#define JB_PC 5 + +typedef struct __kdb_jmp_buf { + unsigned long regs[6]; +} kdb_jmp_buf; + +extern int kdb_setjmp(kdb_jmp_buf *); +extern void kdb_longjmp(kdb_jmp_buf *, int); + +extern kdb_jmp_buf kdbjmpbuf[]; + +#endif /* KDSUPPORT */ Index: 2.2.15pre19/arch/i386/kdb/modules/Makefile --- 2.2.15pre19/arch/i386/kdb/modules/Makefile Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/arch/i386/kdb/modules/Makefile Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/s/c/41_Makefile 1.1 644) @@ -0,0 +1,17 @@ +# +# Makefile for i386-specific kdb files.. +# +# Copyright 1999, Silicon Graphics Inc. +# +# Written April 1999 by Scott Lurndal at Silicon Graphics, Inc. +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +#O_TARGET := kdbm_vm.o + +M_OBJS := kdbm_vm.o + +include $(TOPDIR)/Rules.make Index: 2.2.15pre19/arch/i386/kdb/modules/kdbm_vm.c --- 2.2.15pre19/arch/i386/kdb/modules/kdbm_vm.c Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/arch/i386/kdb/modules/kdbm_vm.c Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/s/c/40_kdbm_vm.c 1.1 644) @@ -0,0 +1,401 @@ +#include +#include +#include +#include + +#include <../drivers/scsi/scsi.h> +#include <../drivers/scsi/hosts.h> + +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) { + printk("%s ", tp->name); + } + } + printk("\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", "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("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); + + return 0; +} + +void +cleanup_module(void) +{ + kdb_unregister("vm"); + kdb_unregister("modlist"); + kdb_unregister("modsyms"); + kdb_unregister("filp"); + kdb_unregister("sh"); + kdb_unregister("sd"); + kdb_unregister("sc"); +} Index: 2.2.15pre19/arch/i386/kernel/entry.S --- 2.2.15pre19/arch/i386/kernel/entry.S Thu, 20 Apr 2000 22:09:39 +1000 kaos (linux-2.2/E/b/43_entry.S 1.1.2.2 644) +++ 2.2.15pre19/arch/i386/kernel/entry.S Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/E/b/43_entry.S 1.1.2.3 644) @@ -161,6 +161,19 @@ GET_CURRENT(%ebx) jmp ret_from_sys_call +#if defined(CONFIG_KDB) +ENTRY(kdb_call) + pushl %eax # save orig EAX + SAVE_ALL + pushl %esp # struct pt_regs + pushl $0 # error_code + pushl $7 # KDB_REASON_INT + call kdb + addl $12,%esp # remove args + RESTORE_ALL + +#endif + /* * Return to user mode is not as complex as all this looks, * but we want the default path for a system call return to @@ -298,6 +311,13 @@ addl $4,%esp ret +#if defined(CONFIG_KDB) +ENTRY(machine_check) + pushl $0 + pushl $ SYMBOL_NAME(do_machine_check) + jmp error_code +#endif + ENTRY(debug) pushl $0 pushl $ SYMBOL_NAME(do_debug) @@ -333,10 +353,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) Index: 2.2.15pre19/arch/i386/kernel/i386_ksyms.c --- 2.2.15pre19/arch/i386/kernel/i386_ksyms.c Wed, 20 Oct 1999 19:27:54 +1000 keith (linux-2.2/E/b/26_i386_ksyms 1.6 644) +++ 2.2.15pre19/arch/i386/kernel/i386_ksyms.c Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/E/b/26_i386_ksyms 1.6.1.1 644) @@ -17,6 +17,9 @@ #include #include #include +#if defined(CONFIG_KDB) +#include +#endif extern void dump_thread(struct pt_regs *, struct user *); extern int dump_fpu(elf_fpregset_t *); @@ -118,4 +121,18 @@ #ifdef CONFIG_VT EXPORT_SYMBOL(screen_info); +#endif + +#if defined(CONFIG_KDB) +EXPORT_SYMBOL(kdb_register); +EXPORT_SYMBOL(kdb_unregister); +EXPORT_SYMBOL(kdbgetword); +EXPORT_SYMBOL(kdbgetularg); +EXPORT_SYMBOL(kdbgetenv); +EXPORT_SYMBOL(kdbgetintenv); +EXPORT_SYMBOL(kdbgetaddrarg); +EXPORT_SYMBOL(kdb); +EXPORT_SYMBOL(kdbgetsymval); +EXPORT_SYMBOL(kdbnearsym); +EXPORT_SYMBOL(kdb_printf); #endif Index: 2.2.15pre19/arch/i386/kernel/irq.c --- 2.2.15pre19/arch/i386/kernel/irq.c Thu, 20 Apr 2000 22:09:39 +1000 kaos (linux-2.2/E/b/31_irq.c 1.2.2.5 644) +++ 2.2.15pre19/arch/i386/kernel/irq.c Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/E/b/31_irq.c 1.2.2.6 644) @@ -323,6 +323,9 @@ BUILD_SMP_INTERRUPT(invalidate_interrupt) BUILD_SMP_INTERRUPT(stop_cpu_interrupt) BUILD_SMP_INTERRUPT(call_function_interrupt) +#if defined(CONFIG_KDB) +BUILD_SMP_INTERRUPT(kdb_interrupt) +#endif /* CONFIG_KDB */ BUILD_SMP_INTERRUPT(spurious_interrupt) /* @@ -1080,7 +1083,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]); } @@ -1112,6 +1119,11 @@ /* IPI vector for APIC spurious interrupts */ set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); + +#if defined(__SMP__) && defined(CONFIG_KDB) + /* IPI vector for Kernel Debugger */ + set_intr_gate(KDB_VECTOR, kdb_interrupt); +#endif /* CONFIG_SMP */ #endif request_region(0x20,0x20,"pic1"); request_region(0xa0,0x20,"pic2"); Index: 2.2.15pre19/arch/i386/kernel/irq.h --- 2.2.15pre19/arch/i386/kernel/irq.h Wed, 05 Jan 2000 09:26:04 +1100 keith (linux-2.2/E/b/24_irq.h 1.2.1.3.3.1 644) +++ 2.2.15pre19/arch/i386/kernel/irq.h Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/E/b/24_irq.h 1.2.1.3.3.2 644) @@ -50,6 +50,9 @@ #define FIRST_EXTERNAL_VECTOR 0x20 #define SYSCALL_VECTOR 0x80 +#if defined(CONFIG_KDB) +#define KDBENTER_VECTOR 0x81 +#endif /* * Vectors 0x20-0x2f are used for ISA interrupts. @@ -65,6 +68,9 @@ #define RESCHEDULE_VECTOR 0x30 #define INVALIDATE_TLB_VECTOR 0x31 #define STOP_CPU_VECTOR 0x40 +#if defined(CONFIG_KDB) +#define KDB_VECTOR 0x42 +#endif /* CONFIG_KDB */ #define LOCAL_TIMER_VECTOR 0x41 #define CALL_FUNCTION_VECTOR 0x50 Index: 2.2.15pre19/arch/i386/kernel/smp.c --- 2.2.15pre19/arch/i386/kernel/smp.c Thu, 20 Apr 2000 22:09:39 +1000 kaos (linux-2.2/E/b/28_smp.c 1.2.2.1.1.5.3.1.2.1 644) +++ 2.2.15pre19/arch/i386/kernel/smp.c Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/E/b/28_smp.c 1.2.2.1.1.5.3.1.2.2 644) @@ -42,10 +42,14 @@ #include #include -#include "irq.h" - #define JIFFIE_TIMEOUT 100 +#if defined(CONFIG_KDB) +#include +#endif + +#include "irq.h" + extern void update_one_process( struct task_struct *p, unsigned long ticks, unsigned long user, unsigned long system, int cpu); @@ -104,6 +108,9 @@ unsigned long cpu_present_map = 0; /* Bitmask of physically existing CPUs */ unsigned long cpu_online_map = 0; /* Bitmask of currently online CPUs */ +#if defined(__SMP__) +unsigned long smp_kdb_wait = 0; /* Bitmask of waiters */ +#endif /* CONFIG_SMP */ int smp_num_cpus = 1; /* Total count of live CPUs */ int smp_threads_ready=0; /* Set when the idlers are all forked */ volatile int cpu_number_map[NR_CPUS]; /* which CPU maps to which logical number */ @@ -1100,6 +1107,9 @@ */ int __init start_secondary(void *unused) { +#if defined(CONFIG_KDB) + kdb_enablemce(); +#endif /* * Dont put anything before smp_callin(), SMP * booting is too fragile that we want to limit the @@ -1749,6 +1759,15 @@ cfg = __prepare_ICR(shortcut, vector); +#if defined(CONFIG_KDB) + if (vector == KDB_VECTOR) { + /* + * Set up kdb IPI to be delivered as a + * Non-Maskable interrupt. + */ + cfg = (cfg&~APIC_DEST_DM_MASK)|APIC_DEST_DM_NMI; + } +#endif /* * Send the IPI. The write to APIC_ICR fires this off. */ @@ -1978,6 +1997,21 @@ return 0; } +#if defined(CONFIG_KDB) +void smp_kdb_stop(int all) +{ + if (all) { + smp_kdb_wait = 0xffffffff; + + clear_bit(smp_processor_id(), &smp_kdb_wait); + send_IPI_allbutself(KDB_VECTOR); + } else { + set_bit(smp_processor_id(), &smp_kdb_wait); + send_IPI_self(KDB_VECTOR); + } +} +#endif + /* * Local timer interrupt handler. It does both profiling and * process statistics/rescheduling. @@ -2113,6 +2147,52 @@ { stop_this_cpu(); } + +#if defined(CONFIG_KDB) +asmlinkage void smp_kdb_interrupt(int a) +{ +#if 0 + /* + * The stub function which calls this routine pushes + * the register context on the stack prior to calling + * this routine. No arguments are pushed on the stack, + * so taking the address of a non-existant first argument + * will obtain the address of the preserved register context + * + * This is a gross hack, but it works and doesn't require + * changes to BUILD_SMP_INTERRUPT. + */ + struct pt_regs *regs = (struct pt_regs *)&a; + + ack_APIC_irq(); + + /* + * Wait for kdb to set us free. + */ +#if 0 + printk("stopping cpu %d for kdb\n", smp_processor_id()); +#endif + + while (test_bit(smp_processor_id(), &smp_kdb_wait)) + ; +#if 0 + printk("restarting cpu %d for kdb\n", smp_processor_id()); +#endif + + /* + * If we are the new CPU, call the kernel debugger here. + */ + if (kdb_new_cpu == smp_processor_id()) { + kdb(KDB_REASON_SWITCH, 0, regs); + } + + /* + * Re-establish any global breakpoint state + */ + kdb_bp_install(); +#endif +} +#endif /* CONFIG_KDB */ asmlinkage void smp_call_function_interrupt(void) { Index: 2.2.15pre19/arch/i386/kernel/traps.c --- 2.2.15pre19/arch/i386/kernel/traps.c Tue, 23 Feb 1999 17:04:00 +1100 keith (linux-2.2/E/b/41_traps.c 1.2 644) +++ 2.2.15pre19/arch/i386/kernel/traps.c Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/E/b/41_traps.c 1.2.5.1 644) @@ -26,6 +26,10 @@ #include #endif +#if defined(CONFIG_KDB) +#include +#endif + #include #include #include @@ -33,6 +37,9 @@ #include #include #include +#if defined(CONFIG_KDB) +#include +#endif #include @@ -45,6 +52,9 @@ #include "irq.h" asmlinkage int system_call(void); +#if defined(CONFIG_KDB) +asmlinkage int kdb_call(void); +#endif asmlinkage void lcall7(void); struct desc_struct default_ldt = { 0, 0 }; @@ -199,6 +209,9 @@ { console_verbose(); spin_lock_irq(&die_lock); +#if defined(CONFIG_KDB) + kdb(KDB_REASON_PANIC, err, regs); +#endif printk("%s: %04lx\n", str, err & 0xffff); show_registers(regs); spin_unlock_irq(&die_lock); @@ -237,7 +250,9 @@ DO_ERROR(11, SIGBUS, "segment not present", segment_not_present, current) DO_ERROR(12, SIGBUS, "stack segment", stack_segment, current) DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current) +#if !defined(CONFIG_KDB) DO_ERROR(18, SIGSEGV, "reserved", reserved, current) +#endif /* I don't have documents for this but it does seem to cover the cache flush from user space exception some people get. */ DO_ERROR(19, SIGSEGV, "cache flush denied", cache_flush_denied, current) @@ -317,7 +332,22 @@ return; } #endif +#if defined(CONFIG_KDB) && defined(CONFIG_SMP) + /* + * Call KDB to see if we expected this NMI due to an IPI + * operation. If so, handle it, else we'll fall through + * and call kdb directly. + */ + if (kdb_ipi(regs)) { + return; + } +#endif /* CONFIG_KDB */ + printk("Uhhuh. NMI received for unknown reason %02x.\n", reason); + +#if defined(CONFIG_KDB) + kdb(KDB_REASON_NMI, reason, regs); +#endif printk("Dazed and confused, but trying to continue\n"); printk("Do you have a strange power saving mode enabled?\n"); } @@ -336,6 +366,25 @@ unknown_nmi_error(reason, regs); } +#if defined(CONFIG_KDB) +asmlinkage void do_machine_check(struct pt_regs * regs, long error_code) +{ + u32 i, lv, hv; + + rdmsr(MCG_STATUS_MSR, lv, hv); + printk("machine check status 0x%x\n", lv); + rdmsr(MCG_CAP, lv, hv); + printk("%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); + printk("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 @@ -397,9 +446,15 @@ return; clear_dr7: + +#if defined(CONFIG_KDB) + kdb(KDB_REASON_DEBUG, error_code, regs); +#else + __asm__("movl %0,%%db7" : /* no output */ : "r" (0)); +#endif /* CONFIG_KDB */ return; clear_TF: @@ -692,6 +747,18 @@ set_trap_gate(16,&coprocessor_error); set_trap_gate(17,&alignment_check); set_system_gate(SYSCALL_VECTOR,&system_call); +#if defined(CONFIG_KDB) + { + extern void *machine_check; + set_trap_gate(18,&machine_check); + } + kdb_enablemce(); + /* + * A trap gate, used by the kernel to enter the + * debugger preserving all registers. + */ + set_trap_gate(KDBENTER_VECTOR,&kdb_call); +#endif /* set up GDT task & ldt entries */ set_tss_desc(0, &init_task.tss); Index: 2.2.15pre19/arch/i386/vmlinux.lds.S --- 2.2.15pre19/arch/i386/vmlinux.lds.S Tue, 10 Aug 1999 11:02:21 +1000 keith (linux-2.2/U/b/29_vmlinux.ld 1.1 644) +++ 2.2.15pre19/arch/i386/vmlinux.lds.S Thu, 20 Apr 2000 22:49:11 +1000 kaos (linux-2.2/U/b/29_vmlinux.ld 1.2 644) @@ -26,6 +26,10 @@ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + _etext = .; /* End of text section */ .data : { /* Data */ Index: 2.2.15pre19/arch/m68k/vmlinux.lds --- 2.2.15pre19/arch/m68k/vmlinux.lds Fri, 05 Feb 1999 09:47:48 +1100 keith (linux-2.2/s/b/37_vmlinux.ld 1.1 644) +++ 2.2.15pre19/arch/m68k/vmlinux.lds Thu, 20 Apr 2000 22:49:11 +1000 kaos (linux-2.2/s/b/37_vmlinux.ld 1.2 644) @@ -24,6 +24,10 @@ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + _etext = .; /* End of text section */ .data : { /* Data */ Index: 2.2.15pre19/arch/s390/vmlinux.lds --- 2.2.15pre19/arch/s390/vmlinux.lds Wed, 05 Jan 2000 09:26:04 +1100 keith (linux-2.2/l/c/32_vmlinux.ld 1.1 644) +++ 2.2.15pre19/arch/s390/vmlinux.lds Thu, 20 Apr 2000 22:49:11 +1000 kaos (linux-2.2/l/c/32_vmlinux.ld 1.2 644) @@ -26,6 +26,10 @@ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + _etext = .; /* End of text section */ .data : { /* Data */ Index: 2.2.15pre19/arch/sparc/vmlinux.lds --- 2.2.15pre19/arch/sparc/vmlinux.lds Wed, 24 Mar 1999 10:22:04 +1100 keith (linux-2.2/A/b/0_vmlinux.ld 1.2 644) +++ 2.2.15pre19/arch/sparc/vmlinux.lds Thu, 20 Apr 2000 22:49:11 +1000 kaos (linux-2.2/A/b/0_vmlinux.ld 1.3 644) @@ -32,6 +32,10 @@ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + . = ALIGN(32); .data.cacheline_aligned : { *(.data.cacheline_aligned) } Index: 2.2.15pre19/arch/sparc64/vmlinux.lds --- 2.2.15pre19/arch/sparc64/vmlinux.lds Fri, 05 Feb 1999 09:47:48 +1100 keith (linux-2.2/s/b/3_vmlinux.ld 1.1 644) +++ 2.2.15pre19/arch/sparc64/vmlinux.lds Thu, 20 Apr 2000 22:49:11 +1000 kaos (linux-2.2/s/b/3_vmlinux.ld 1.2 644) @@ -35,6 +35,9 @@ __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; __kstrtab : { *(.kstrtab) } + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; . = ALIGN(8192); __init_begin = .; .text.init : { *(.text.init) } Index: 2.2.15pre19/drivers/char/keyboard.c --- 2.2.15pre19/drivers/char/keyboard.c Tue, 10 Aug 1999 11:02:21 +1000 keith (linux-2.2/h/b/34_keyboard.c 1.1.1.2 644) +++ 2.2.15pre19/drivers/char/keyboard.c Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/h/b/34_keyboard.c 1.1.1.3 644) @@ -42,6 +42,13 @@ #include #include #include +#if defined(CONFIG_KDB) +#include +#endif + +#if defined(CONFIG_KDB) +#include +#endif #define SIZE(x) (sizeof(x)/sizeof((x)[0])) @@ -242,6 +249,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 #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ if (keycode == SYSRQ_KEY) { Index: 2.2.15pre19/drivers/char/serial.c --- 2.2.15pre19/drivers/char/serial.c Thu, 20 Apr 2000 22:09:39 +1000 kaos (linux-2.2/h/b/20_serial.c 1.2.1.2.2.1.2.1 644) +++ 2.2.15pre19/drivers/char/serial.c Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/h/b/20_serial.c 1.2.1.2.2.1.2.2 644) @@ -375,8 +375,26 @@ mark_bh(SERIAL_BH); } +#if defined(CONFIG_KDB) +#include +/* + * kdb_serial_line records the serial line number of the + * first serial console. kdb_info will be set upon receipt + * of the first ^A (which cannot happen until the port is + * opened and the interrupt handler attached). To enter + * kdb before this on a serial console-only system, you must + * use the 'kdb' flag to lilo and set the appropriate breakpoints. + */ + +static int kdb_serial_line = -1; +#endif /* CONFIG_KDB */ + static _INLINE_ void receive_chars(struct async_struct *info, +#if defined(CONFIG_KDB) + int *status, struct pt_regs * regs) +#else int *status) +#endif { struct tty_struct *tty = info->tty; unsigned char ch; @@ -386,6 +404,13 @@ icount = &info->state->icount; do { ch = serial_inp(info, UART_RX); +#if defined(CONFIG_KDB) + if ((info->line == kdb_serial_line) + && (ch == 1)) /* CNTRL-A */ { + kdb(KDB_REASON_KEYBOARD, 0, regs); + break; + } +#endif if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; *tty->flip.char_buf_ptr = ch; @@ -612,7 +637,11 @@ printk("status = %x...", status); #endif if (status & UART_LSR_DR) +#if defined(CONFIG_KDB) + receive_chars(info, &status, regs); +#else receive_chars(info, &status); +#endif check_modem_status(info); if (status & UART_LSR_THRE) transmit_chars(info, 0); @@ -676,7 +705,11 @@ printk("status = %x...", status); #endif if (status & UART_LSR_DR) +#if defined(CONFIG_KDB) + receive_chars(info, &status, regs); +#else receive_chars(info, &status); +#endif check_modem_status(info); if (status & UART_LSR_THRE) transmit_chars(info, 0); @@ -739,7 +772,11 @@ printk("status = %x...", status); #endif if (status & UART_LSR_DR) +#if defined(CONFIG_KDB) + receive_chars(info, &status, regs); +#else receive_chars(info, &status); +#endif check_modem_status(info); if (status & UART_LSR_THRE) transmit_chars(info, 0); @@ -3365,6 +3402,7 @@ unsigned i; ser = rs_table + co->index; + /* * First save the IER then disable the interrupts */ @@ -3541,6 +3579,18 @@ */ if (inb(ser->port + UART_LSR) == 0xff) return -1; + +#if defined(CONFIG_KDB) + /* + * Remember the line number of the first serial + * console. We'll make this the kdb serial console too. + */ + if (kdb_serial_line == -1) { + kdb_serial_line = co->index; + kdb_port = ser->port; + } +#endif /* CONFIG_KDB */ + return 0; } Index: 2.2.15pre19/include/asm-i386/i82489.h --- 2.2.15pre19/include/asm-i386/i82489.h Fri, 05 Feb 1999 09:47:48 +1100 keith (linux-2.2/B/15_i82489.h 1.1 644) +++ 2.2.15pre19/include/asm-i386/i82489.h Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/B/15_i82489.h 1.2 644) @@ -57,6 +57,7 @@ #define APIC_DEST_DM_NMI 0x00400 #define APIC_DEST_DM_INIT 0x00500 #define APIC_DEST_DM_STARTUP 0x00600 +#define APIC_DEST_DM_MASK 0x00700 #define APIC_DEST_VECTOR_MASK 0x000FF #define APIC_ICR2 0x310 #define GET_APIC_DEST_FIELD(x) (((x)>>24)&0xFF) Index: 2.2.15pre19/include/asm-i386/keyboard.h --- 2.2.15pre19/include/asm-i386/keyboard.h Tue, 10 Aug 1999 11:02:21 +1000 keith (linux-2.2/A/39_keyboard.h 1.3 644) +++ 2.2.15pre19/include/asm-i386/keyboard.h Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/A/39_keyboard.h 1.4 644) @@ -38,6 +38,9 @@ #define kbd_sysrq_xlate pckbd_sysrq_xlate #define SYSRQ_KEY 0x54 +#if defined(CONFIG_KDB) +#define E1_PAUSE 119 /* PAUSE key */ +#endif /* resource allocation */ #define kbd_request_region() request_region(0x60, 16, "keyboard") Index: 2.2.15pre19/include/asm-i386/msr.h --- 2.2.15pre19/include/asm-i386/msr.h Wed, 12 May 1999 17:22:01 +1000 keith (linux-2.2/P/b/33_msr.h 1.1 644) +++ 2.2.15pre19/include/asm-i386/msr.h Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/P/b/33_msr.h 1.2 644) @@ -28,3 +28,12 @@ : "=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 Index: 2.2.15pre19/include/asm-i386/ptrace.h --- 2.2.15pre19/include/asm-i386/ptrace.h Fri, 05 Feb 1999 09:47:48 +1100 keith (linux-2.2/B/31_ptrace.h 1.1 644) +++ 2.2.15pre19/include/asm-i386/ptrace.h Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/B/31_ptrace.h 1.1.2.1 644) @@ -47,6 +47,29 @@ #define PTRACE_GETFPREGS 14 #define PTRACE_SETFPREGS 15 +enum EFLAGS { + EF_CF = 0x00000001, + EF_PF = 0x00000004, + EF_AF = 0x00000010, + EF_ZF = 0x00000040, + EF_SF = 0x00000080, + EF_TF = 0x00000100, + EF_IE = 0x00000200, + EF_DF = 0x00000400, + EF_OF = 0x00000800, + EF_IOPL = 0x00003000, + EF_IOPL_RING0 = 0x00000000, + EF_IOPL_RING1 = 0x00001000, + EF_IOPL_RING2 = 0x00002000, + EF_NT = 0x00004000, // nested task + EF_RF = 0x00010000, // resume + EF_VM = 0x00020000, // virtual mode + EF_AC = 0x00040000, // alignment + EF_VIF = 0x00080000, // virtual interrupt + EF_VIP = 0x00100000, // virtual interrupt pending + EF_ID = 0x00200000, // id +}; + #ifdef __KERNEL__ #define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->xcs)) #define instruction_pointer(regs) ((regs)->eip) Index: 2.2.15pre19/include/linux/kallsyms.h --- 2.2.15pre19/include/linux/kallsyms.h Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/include/linux/kallsyms.h Fri, 21 Apr 2000 20:52:46 +1000 kaos (linux-2.2/t/c/12_kallsyms.h 1.2 644) @@ -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: kallsyms.h 1.7 Fri, 21 Apr 2000 20:48:56 +1000 kaos $" + +#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 */ Index: 2.2.15pre19/include/linux/kdb.h --- 2.2.15pre19/include/linux/kdb.h Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/include/linux/kdb.h Fri, 21 Apr 2000 18:04:41 +1000 kaos (linux-2.2/s/c/39_kdb.h 1.2 644) @@ -0,0 +1,213 @@ +/* + * Kernel Debugger + * + * Copyright 1999, Silicon Graphics, Inc. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Changes: + * Keith Owens 2000/04/20 + * Use kallsyms for symbol handling. + */ + +#if !defined(__KDB_H) +#define __KDB_H + +#include "bfd.h" + +#define KDB_MAJOR_VERSION 0 +#define KDB_MINOR_VERSION 6 + +/* + * Kernel Debugger Error codes + */ + +#define KDB_NOTFOUND -1 +#define KDB_GO -2 +#define KDB_ARGCOUNT -3 +#define KDB_BADWIDTH -4 +#define KDB_BADRADIX -5 +#define KDB_NOTENV -6 +#define KDB_NOENVVALUE -7 +#define KDB_NOTIMP -8 +#define KDB_ENVFULL -9 +#define KDB_ENVBUFFULL -10 +#define KDB_TOOMANYBPT -11 +#define KDB_TOOMANYDBREGS -12 +#define KDB_DUPBPT -13 +#define KDB_BPTNOTFOUND -14 +#define KDB_BADMODE -15 +#define KDB_BADINT -16 +#define KDB_INVADDRFMT -17 +#define KDB_BADREG -18 +#define KDB_CPUSWITCH -19 +#define KDB_BADCPUNUM -20 +#define KDB_BADLENGTH -21 +#define KDB_NOBP -22 + + /* + * XXX - machine dependent. + */ +#define KDB_ENTER() asm("\tint $129\n") + + /* + * kdb_active is initialized to zero, and is set + * to KDB_REASON_xxx whenever the kernel debugger is entered. + */ +extern 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 kdbgetword. No further error messages + * will be printed until this flag is reset. Used to prevent + * an illegal address from causing voluminous error messages. + */ +#define KDB_FLAG_SUPRESS 0x00000004 + /* + * KDB_FLAG_FAULT is set when handling a debug register instruction + * fault. The backtrace code needs to know. + */ +#define KDB_FLAG_FAULT 0x00000008 + /* + * 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 + +extern volatile int kdb_flags; + +extern int kdb_nextline; + + /* + * External entry point for the kernel debugger. The pt_regs + * at the time of entry are supplied along with the reason for + * entry to the kernel debugger. + */ + +struct pt_regs; +extern int kdb(int reason, int error_code, struct pt_regs *); +#define KDB_REASON_ENTER 1 /* call debugger from source */ +#define KDB_REASON_FAULT 2 /* called from fault, eframe valid */ +#define KDB_REASON_BREAK 3 /* called from int 3, eframe valid */ +#define KDB_REASON_DEBUG 4 /* Called from int #DB, eframe valid */ +#define KDB_REASON_PANIC 5 /* Called from panic(), eframe valid */ +#define KDB_REASON_SWITCH 6 /* Called via CPU switch */ +#define KDB_REASON_INT 7 /* Called via int 129 */ +#define KDB_REASON_KEYBOARD 8 /* Called via keyboard interrupt */ +#define KDB_REASON_NMI 9 /* Called due to NonMaskable Int */ + + /* + * The entire contents of this file from this point forward are + * private to the kernel debugger. They are subject to change + * without notice. + */ + + /* + * Breakpoint state + */ + +typedef struct _kdb_bp { + bfd_vma bp_addr; /* Address breakpoint is present at */ + unsigned char bp_prevbyte; /* Byte which the int3 replaced */ + + unsigned int bp_regtype:1; /* Uses a debug register (0-4) */ + unsigned int bp_enabled:1; /* Breakpoint is active in register */ + unsigned int bp_global:1; /* Global to all processors */ + unsigned int bp_free:1; /* This entry is available */ + unsigned int bp_data:1; /* Data breakpoint */ + unsigned int bp_write:1; /* Write data breakpoint */ + unsigned int bp_mode:2; /* 0=inst, 1=write, 2=io, 3=read */ + unsigned int bp_length:2; /* 0=1 byte, 1=2 bytes, 2=BAD, 3=4 */ + + int bp_reg; /* Register that this breakpoint uses */ + int bp_cpu; /* Cpu # (if bp_global == 0) */ +} kdb_bp_t; + +extern void kdb_global(int); /* Set global actions */ + + /* + * External Function Declarations + */ +extern char *kdbgetenv(const char *); +extern int kdbgetintenv(const char *, int *); +extern int kdbgetularg(const char *, unsigned long *); +extern int kdbgetaddrarg(int, const char**, int*, unsigned long *, long *, char **, struct pt_regs *); +extern unsigned long kdbgetword(unsigned long, int); +extern void kdb_id1(unsigned long); +extern void kdb_disinit(void); +extern void kdb_enablemce(void); + +#if defined(__SMP__) + /* + * Kernel debugger non-maskable IPI handler. + */ +extern int kdb_ipi(struct pt_regs *); +#endif + + /* + * External command function declarations + */ + +extern int kdb_id (int argc, const char **argv, const char **envp, struct pt_regs *); +extern int kdb_bp (int argc, const char **argv, const char **envp, struct pt_regs *); +extern int kdb_bc (int argc, const char **argv, const char **envp, struct pt_regs *); +extern int kdb_bt (int argc, const char **argv, const char **envp, struct pt_regs *); +extern int kdb_ss (int argc, const char **argv, const char **envp, struct pt_regs *); + + /* + * Symbol table format returned by kallsyms. + */ + +typedef struct __symtab { + 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; + } __ksymtab_t; + +extern int kdbgetsymval(const char *, __ksymtab_t *); +extern int kdbnearsym(unsigned long, __ksymtab_t *); + +typedef int (*kdb_func)(int, const char **, const char **, struct pt_regs*); + +extern int kdb_register(char *, kdb_func, char *, char *, short); +extern int kdb_unregister(char *); + +extern int kdb_printf(const char *,...); +#endif /* __KDB_H */ Index: 2.2.15pre19/include/linux/module.h --- 2.2.15pre19/include/linux/module.h Thu, 20 Apr 2000 22:09:39 +1000 kaos (linux-2.2/F/51_module.h 1.1.2.1 644) +++ 2.2.15pre19/include/linux/module.h Thu, 20 Apr 2000 22:49:11 +1000 kaos (linux-2.2/F/51_module.h 1.1.2.2 644) @@ -77,11 +77,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 Index: 2.2.15pre19/include/linux/smp.h --- 2.2.15pre19/include/linux/smp.h Mon, 14 Jun 1999 17:28:27 +1000 keith (linux-2.2/C/0_smp.h 1.1.1.2 644) +++ 2.2.15pre19/include/linux/smp.h Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/C/0_smp.h 1.1.1.3 644) @@ -54,6 +54,10 @@ extern int smp_num_cpus; +#if defined(CONFIG_KDB) +extern volatile unsigned long smp_kdb_wait; +extern void smp_kdb_stop(int); +#endif /* CONFIG_KDB */ extern volatile unsigned long smp_msg_data; extern volatile int smp_src_cpu; extern volatile int smp_msg_id; Index: 2.2.15pre19/init/main.c --- 2.2.15pre19/init/main.c Thu, 20 Apr 2000 22:09:39 +1000 kaos (linux-2.2/h/35_main.c 1.2.1.1.1.5.4.1.2.1 644) +++ 2.2.15pre19/init/main.c Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/h/35_main.c 1.2.1.1.1.5.4.1.2.2 644) @@ -54,6 +54,10 @@ extern void nubus_init(void); #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. @@ -1182,6 +1186,12 @@ console_loglevel = 10; continue; } +#if defined(CONFIG_KDB) + if (!strcmp(line,"kdb")) { + kdb_flags = KDB_FLAG_EARLYKDB; + continue; + } +#endif /* CONFIG_KDB */ if (!strncmp(line,"init=",5)) { line += 5; execute_command = line; @@ -1310,6 +1320,13 @@ } #endif mem_init(memory_start,memory_end); +#if defined(CONFIG_KDB) + { + extern void kdb_init(void); + + kdb_init(); + } +#endif kmem_cache_sizes_init(); #ifdef CONFIG_3215_CONSOLE con3215_activate(); @@ -1335,6 +1352,10 @@ check_bugs(); printk("POSIX conformance testing by UNIFIX\n"); +#if defined(CONFIG_KDB) + if (kdb_flags & KDB_FLAG_EARLYKDB) + KDB_ENTER(); +#endif /* * We count on the initial thread going ok * Like idlers init is an unlocked kernel thread, which will @@ -1515,6 +1536,10 @@ (void) dup(0); (void) dup(0); +#if defined(CONFIG_KDB) + if (kdb_flags & KDB_FLAG_EARLYKDB) + KDB_ENTER(); +#endif /* * We try each of these until one succeeds. * Index: 2.2.15pre19/kernel/Makefile --- 2.2.15pre19/kernel/Makefile Fri, 05 Feb 1999 09:47:48 +1100 keith (linux-2.2/h/28_Makefile 1.1 644) +++ 2.2.15pre19/kernel/Makefile Fri, 21 Apr 2000 18:19:45 +1000 kaos (linux-2.2/h/28_Makefile 1.1.4.1 644) @@ -21,6 +21,10 @@ O_OBJS += kmod.o endif +ifeq ($(CONFIG_KALLSYMS),y) +O_OBJS += kallsyms.o +endif + ifeq ($(CONFIG_MODULES),y) OX_OBJS += ksyms.o endif Index: 2.2.15pre19/kernel/kallsyms.c --- 2.2.15pre19/kernel/kallsyms.c Fri, 21 Apr 2000 22:03:11 +1000 kaos () +++ 2.2.15pre19/kernel/kallsyms.c Fri, 21 Apr 2000 19:51:27 +1000 kaos (linux-2.2/t/c/11_kallsyms.c 1.1.1.1 644) @@ -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: kallsyms.c 1.1 Fri, 21 Apr 2000 19:31:54 +1000 kaos $" + +/* + 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 *local_module_list; + +static void 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) + local_module_list = *((struct module **)(ka_sym->symbol_addr)); + break; + } + } +} + +static void __inline__ do_first_time(void) +{ + static int first_time = 1; + if (first_time) + 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; + + do_first_time(); + + /* Restart? */ + m = local_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; + + do_first_time(); + + for (m = local_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); +} Index: 2.2.15pre19/kernel/ksyms.c --- 2.2.15pre19/kernel/ksyms.c Thu, 20 Apr 2000 22:09:39 +1000 kaos (linux-2.2/h/19_ksyms.c 1.2.1.3.3.2 644) +++ 2.2.15pre19/kernel/ksyms.c Fri, 21 Apr 2000 18:19:45 +1000 kaos (linux-2.2/h/19_ksyms.c 1.2.1.3.3.3.1.1 644) @@ -46,6 +46,9 @@ #ifdef CONFIG_KMOD #include #endif +#ifdef CONFIG_KALLSYMS +#include +#endif extern char *get_options(char *str, int *ints); extern void set_device_ro(kdev_t dev,int flag); @@ -81,6 +84,15 @@ EXPORT_SYMBOL(get_module_symbol); #endif EXPORT_SYMBOL(get_options); + +#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); Index: 2.2.15pre19/kernel/module.c --- 2.2.15pre19/kernel/module.c Thu, 20 Apr 2000 22:09:39 +1000 kaos (linux-2.2/h/18_module.c 1.1.4.1 644) +++ 2.2.15pre19/kernel/module.c Fri, 21 Apr 2000 18:19:45 +1000 kaos (linux-2.2/h/18_module.c 1.1.4.2.1.1 644) @@ -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); Index: 2.2.15pre19/mm/vmalloc.c --- 2.2.15pre19/mm/vmalloc.c Thu, 29 Apr 1999 10:14:10 +1000 keith (linux-2.2/g/49_vmalloc.c 1.1.1.1 644) +++ 2.2.15pre19/mm/vmalloc.c Thu, 20 Apr 2000 23:09:20 +1000 kaos (linux-2.2/g/49_vmalloc.c 1.1.1.2 644) @@ -11,6 +11,43 @@ static struct vm_struct * vmlist = NULL; +#if defined(CONFIG_KDB) +/* kdb_vmlist_check + * Check to determine if an address is within a vmalloced range. + * Parameters: + * starta -- Starting address of region to check + * enda -- Ending address of region to check + * Returns: + * 0 -- [starta,enda] not within a vmalloc area + * 1 -- [starta,enda] within a vmalloc area + * Locking: + * None. + * Remarks: + * Shouldn't acquire locks. Always called with all interrupts + * disabled and other cpus halted. Yet, if a breakpoint or fault + * occurs while the vmlist is in an indeterminate state, this + * function could fail. + */ +int +kdb_vmlist_check(unsigned long starta, unsigned long enda) +{ + struct vm_struct *vp; + + for(vp=vmlist; vp; vp = vp->next) { + unsigned long end = (unsigned long)vp->addr + vp->size; + + end -= PAGE_SIZE; /* Unbias for guard page */ + + if ((starta >= (unsigned long)vp->addr) + && (starta < end) + && (enda < end)) { + return 1; + } + } + return 0; +} +#endif + static inline void free_area_pte(pmd_t * pmd, unsigned long address, unsigned long size) { pte_t * pte;