From: Martin Hicks Date: Mon, 07 Dec 2009 11:52:50 -0600 Subject: kdb-v4.4-2.6.32-common-3 References: FATE#303971 X-URL: ftp://oss.sgi.com/www/projects/kdb/download/v4.4/ Patch-mainline: Probably never The KDB common code. Differences: - We remove the binary sysctl. It changes with every release, which makes it useless. - Use debugger_syslog_data as provided by patches/patches.arch/ppc64-xmon-dmesg-printing.patch. It's the same function. Acked-by: Jeff Mahoney --- Documentation/kdb/bt_x86 | 1837 ++++++++ Documentation/kdb/kdb.mm | 492 ++ Documentation/kdb/kdb_bp.man | 197 Documentation/kdb/kdb_bt.man | 315 + Documentation/kdb/kdb_env.man | 46 Documentation/kdb/kdb_ll.man | 134 Documentation/kdb/kdb_md.man | 136 Documentation/kdb/kdb_ps.man | 96 Documentation/kdb/kdb_rd.man | 170 Documentation/kdb/kdb_sr.man | 68 Documentation/kdb/kdb_ss.man | 109 Documentation/kdb/slides | 1382 ++++++ Makefile | 1 drivers/char/keyboard.c | 10 drivers/hid/usbhid/hid-core.c | 40 drivers/hid/usbhid/usbkbd.c | 17 drivers/serial/8250.c | 57 drivers/serial/8250_early.c | 29 drivers/serial/sn_console.c | 73 drivers/usb/core/hcd.c | 71 drivers/usb/core/hcd.h | 11 drivers/usb/host/ehci-hcd.c | 42 drivers/usb/host/ehci-pci.c | 8 drivers/usb/host/ehci-q.c | 222 + drivers/usb/host/ohci-hcd.c | 67 drivers/usb/host/ohci-pci.c | 8 drivers/usb/host/ohci-q.c | 62 drivers/usb/host/uhci-hcd.c | 218 + drivers/usb/host/uhci-q.c | 164 fs/proc/meminfo.c | 145 fs/proc/mmu.c | 16 include/asm-generic/kmap_types.h | 3 include/linux/console.h | 5 include/linux/dis-asm.h | 347 + include/linux/kdb.h | 184 include/linux/kdbprivate.h | 518 ++ include/linux/reboot.h | 7 init/main.c | 32 kdb/ChangeLog | 2040 +++++++++ kdb/Makefile | 43 kdb/kdb_bp.c | 661 +++ kdb/kdb_bt.c | 180 kdb/kdb_cmds | 33 kdb/kdb_id.c | 236 + kdb/kdb_io.c | 859 ++++ kdb/kdbdereference.c | 7258 ++++++++++++++++++++++++++++++++++ kdb/kdbmain.c | 4333 ++++++++++++++++++++ kdb/kdbsupport.c | 1155 +++++ kdb/modules/Makefile | 14 kdb/modules/kdbm_debugtypes.c | 388 + kdb/modules/kdbm_pg.c | 683 +++ kdb/modules/kdbm_sched.c | 57 kdb/modules/kdbm_task.c | 195 kdb/modules/kdbm_vm.c | 1040 ++++ kdb/modules/kdbm_x86.c | 1093 +++++ kdb/modules/lcrash/README | 3 kdb/modules/lcrash/asm/README | 1 kdb/modules/lcrash/asm/kl_dump_ia64.h | 199 kdb/modules/lcrash/asm/kl_types.h | 48 kdb/modules/lcrash/kl_alloc.h | 124 kdb/modules/lcrash/kl_bfd.h | 31 kdb/modules/lcrash/kl_btnode.h | 95 kdb/modules/lcrash/kl_cmp.h | 102 kdb/modules/lcrash/kl_copt.h | 29 kdb/modules/lcrash/kl_debug.h | 168 kdb/modules/lcrash/kl_dump.h | 511 ++ kdb/modules/lcrash/kl_dump_arch.h | 124 kdb/modules/lcrash/kl_dump_ia64.h | 199 kdb/modules/lcrash/kl_dwarfs.h | 27 kdb/modules/lcrash/kl_error.h | 266 + kdb/modules/lcrash/kl_htnode.h | 71 kdb/modules/lcrash/kl_lib.h | 65 kdb/modules/lcrash/kl_libutil.h | 40 kdb/modules/lcrash/kl_mem.h | 104 kdb/modules/lcrash/kl_mem_ia64.h | 149 kdb/modules/lcrash/kl_module.h | 69 kdb/modules/lcrash/kl_queue.h | 89 kdb/modules/lcrash/kl_stabs.h | 122 kdb/modules/lcrash/kl_stringtab.h | 68 kdb/modules/lcrash/kl_sym.h | 131 kdb/modules/lcrash/kl_task.h | 39 kdb/modules/lcrash/kl_typeinfo.h | 199 kdb/modules/lcrash/kl_types.h | 54 kdb/modules/lcrash/klib.h | 480 ++ kdb/modules/lcrash/lc_eval.h | 225 + kernel/exit.c | 3 kernel/kallsyms.c | 23 kernel/kexec.c | 15 kernel/module.c | 19 kernel/panic.c | 9 kernel/sched.c | 109 kernel/signal.c | 49 lib/bug.c | 8 mm/hugetlb.c | 22 mm/mmzone.c | 4 mm/swapfile.c | 22 96 files changed, 31713 insertions(+), 9 deletions(-) --- /dev/null +++ b/Documentation/kdb/bt_x86 @@ -0,0 +1,1837 @@ +Copyright Keith Owens, 2007. + +How the KDB backtrace for x86 works, how to diagnose problems and submit a bug +============================================================================== + +Unlike ia64, x86 architectures do not mandate unwind information in the kernel. +gcc will include some unwind information for C functions, but not for assembler +code. Attempts have been made to add unwind information to the assembler code +by hand, with little success. Eventually Linus rejected the x86 unwind code +because it was breaking too often and destroying useful debugging data. + +Even if the x86 unwinder worked correctly, it would only give an accurate +backtrace, it would not give function arguments. Needless to say, function +arguments are what people really want. To get function arguments requires much +more support from the compiler than simple unwind data, the compiler has to +track line by line where each argument is held and make that data available to +the debugger. Compiling with gcc -g will provide that information, but the +resulting kernel is several times larger than normal. + +Although the gcc -g data can be stored on another machine, there are constructs +in the kernel that cannot be tracked by this method. i386 builds with 4K stacks +and all x86_64 builds have multiple kernel stacks. The compiler knows nothing +about these extra stacks and cannot backtrace through them correctly. The +assembler code in arch/{i386,x86_64}/kernel/entry.S is a maze of twisty logic +paths, some of which eventually jump to common labels. Describing this twisty +logic to an unwinder is very difficult, expecially when you try to describe +where arguments and/or registers are held). + +KDB gets an accurate x86 backtrace and extracts the arguments by performing code +decomposition and analysis at run time. This avoids the need to bloat the +running kernel to several times its normal size with gcc -g data. KDB (unlike +gdb) also knows about the additional kernel stacks and twisty assembler code +paths. + +The x86 backtrace code for i386 is very similar to the x86_64 code, with 80% +common code and data. Most of the differences between the backtrace for the two +architectures is due to the assembler code and stack handling. To avoid code +duplication between KDB patches, the x86 backtrace code is actually stored in +the kdb common patch, in source kdb/kdba_bt_x86.c. kdb/Makefile only builds +kdba_bt_x86.o for i386 or x86_64. Most of the code reads as if the architecture +is x86_64, using register names like rsp and rip. i386 is treated as a subset +of x86_64, with fewer registers and printing the names as esp and eip. When +this documentation refers to rsp and rip, read it as esp and eip for i386. The +20% of code and data that is different in held in two large #ifdef sections, +scan kdba_bt_x86.c for CONFIG_X86_64. Be careful when changing anything in the +architecture specific sections, you will need to review the other architecture +to see if it needs changes as well. + +The idea behind the x86 backtrace is to trace one function at a time, which +gives us the calling function. Then apply the same algorithm to the calling +function until you unwind to the first function in the process. The starting +point for tracing any process is to extract the current stack pointer and +current instruction pointer (rsp and rip). The way that these values are +extracted varies between running tasks and blocked tasks, the method is +described later (Process Starting Point) but ignore it for now, just assume that +we have a starting rsp and rip. + +Given the instruction pointer (rip), we identify the start and end of the kernel +or module function it is in, using the kernel symbol table. This is easy for C +code, it is significantly harder for assembler code because of the twisty code +paths that branch to common labels. The method of identifying the current +function is described later (Identifying The Current Function) but ignore it for +now, just assumes that we have the start and end address of the function plus +its name. + +After the rip has been mapped to a function name with sensible start and end +addresses, the next step is to analyse the code paths in that function. KDB +already has a built in disassembler (copied with slight modifications from +binutils) which knows how to decode each x86 instruction. Instead of +duplicating that logic in kdba_bt_x86, it takes advantage of the fact that you +can override the disassembler's print function, sending the output line to a +buffer instead of printing it. kdba_bt_x86 stills has to decode the buffer but +that is a lot easier than decoding the x86 instruction set. + +The code analysis consists of two main passes. There are example below of the +analysis with basic block (bb) debugging activated (Examples of Basic Block +Debugging Output). + +The first pass (bb_pass1) identifies all the basic blocks in the function. For +our purposes, a basic block has a single entry point and one or more exit +points. The start of the function is the start of basic block 0, all other +blocks are the target of jump instructions (conditional or unconditional) from +within the rest of the code. A block ends with an unconditional jump or with a +terminating instruction such as ret, iret, sysexit, sysret or ud2a (BUG). A +block can also end because the next instruction is the start of a new block +(target of jmp or jcc), in this case there is an implied drop through from one +block to the next. + +Although a call instruction also transfers control, it returns to the next +instruction so call is not treated as a transfer. Instead call is treated as a +normal instruction with side effects, the scratch registers are cleared after a +call. + +At the end of the first pass over the function we have a directed graph that +starts at bb[0]. The nodes of the graph (bb_list[]) are the basic blocks with +their start and end address. The vertices are the jmp or jcc instructions +(bb_jmp_list[]) that transfer control between blocks, plus any implied drop +through transfers between consecutive blocks. This graph can have cycles, many +functions have loops in them which transfer control back to earlier in the code +body. + +The second pass (bb_pass2) runs over the directed graph analysing the effect of +each instruction on the register and memory state. It is important to +understand that the analysis in this pass is an abstract one, it does not use +actual hex values for the register contents, instead it uses symbolic values. +When the basic block code says that "register rsi contains value rax" it means +that whatever value was in rax on entry to the function has also been copied to +register rsi at this point in the logic flow. + +At an abstract level, all C functions start with exactly the same state, each +register contains its own symbolic value (except for the stack pointer, see +later) with no local stack variables defined yet. Assembler functions tend to +have unusual starting points, with some registers and/or memory contents defined +differently on entry. For example, ret_from_intr on i386 already has a struct +pt_regs on its stack, ret_from_intr on x86_64 already has a partial struct +pt_regs plus another two words stacked on top of it. The special starting cases +are listed in the arch specific bb_special_cases[]. + +Once the input state of bb[0] has been defined (including any special cases), +bb_pass2_do_changed_blocks() runs over all the nodes in bb_list[]. Each +instruction in each block is analysed (Tracking the Effects of Instructions) to +see what effect it has on the abstract register state, the analysis of each +instruction is done in bb_usage(). An instruction can copy one register to +another, it can copy a register to stack, move from stack to a register or it +can invalidate the contents of a register or memory location. A general rule in +bb_usage() is that any operation whose results cannot be calculated in terms of +an original input value gives an undefined result. Remember that it is the +abstract value that becomes undefined, moving a constant to a register gives a +defined value for the view of the program but it is undefined as far as the +abstract state is concerned. + +References to data on the stack are a little awkward because the stack pointer +frequently changes. To overcome this, kdba_bt_x86 defines a pseudo register +called the 'original stack pointer' (osp). This always represents the stack +pointer on entry to the function, so on entry rsp contains osp+0x0. As rsp is +modified, it still points at osp, but its offset from osp changes. Copying rsp +to another register (e.g. mov %rsp,%rbp) copies the osp offset as well. At the +point that this function calls the next function down the stack, kdba_bt_x86 +knows the delta from osp to rsp. Applying that delta to the actual value of the +stack pointer gives the stack pointer value on input to the current function, +that location contains the return address so we can go up one stack frame and +repeat the process. + +After doing basic block analysis on the current function, kdba_bt_x86 knows what +the abstract register and memory state is at the point this function was +interrupted or it called the next function down the stack, this is the exit +state. For an interrupt the actual register values are saved in a struct +pt_regs, for a call we have unwound from the KDB interrupt back to the called +function so we have some idea of what the register values are in the called +function. The abstract exit state is merged with the known actual register +values to derive the original stack pointer. That in turn gives us any +registers that were saved on stack. The original stack pointer gives the return +address from the calling function, go up one stack frame and repeat the +analysis. + + +Process Starting Point +====================== + +All backtrace code needs a starting point which defines at least the stack +pointer and instruction pointer, it may define other registers as well. The +first part of kdba_bt_stack() extracts the starting point. Processes can be in +one of three states, running (currently on a cpu), blocked (sleeping or ready to +run but not currently on a cpu) or unknown. + +For running processes, the current rsp and rip are dynamic. Because KDB stops +the entire machine by sending an interrupt to the other cpus, KDB can save the +rsp and rip for each cpu at the point where KDB is entered. This data is held +in array kdb_running_process and is stored by kdb_save_running() and the arch +specific kdba_save_running() functions. When backtracing a running process, KDB +uses the data in kdb_running_process as its starting point. + +For blocked processes we always have the saved rsp, it is held in the process's +thread_info. For i386 blocked processes, thread_info also contains the saved +rip. For x86_64 blocked processes, rip is no longer saved in thread_info, it is +assumed that all blocked processes will resume at assembler label thread_return, +so that rip is used on x86_64. See arch specific kdba_bt_stack_rip(). + +Unknown process state only occurs when the user does 'bt '. +Unlike other bt commands, 'bt ' does not identify any specific +process, instead it identifies a kernel stack. must be inside a +valid kernel stack and must point to a saved rip from a call instruction. +kdba_bt_x86.c uses the common kdba_get_stack_info() and arch specific +kdba_get_stack_info_alternate() functions to check that the address falls within +a valid kernel stack. If the user gives a stack address that does not point to +a saved rip from a call instruction then the backtrace will be garbage. + + +Identifying The Current Function +================================ + +Given a rip value, KDB uses the kallsyms data to find the start of the function +(first address <= rip) and the end of the function (next symbol in kallsyms). +This works for plain C code because gcc only generates one label per function. +It does not work for assembler code or for assembler code embedded in C +functions, because the assembler labels appear as global entries in kallsyms. +For example, arch/i386/kernel/entry.S has function ret_from_exception which +contains three global labels ret_from_intr, check_userspace and +resume_userspace. If rip points to any of those global labels, KDB wants the +start of the real function, i.e. ret_from_exception. In addition, if rip points +to ret_from_exception, KDB wants the end of the function to be after the last +global label in that function, i.e. after resume_userspace. + +The simplest way to handle these unwanted global labels is to list the spurious +assembler labels, which is done in the arch specific array bb_spurious. After +mapping rip to the nearest start and end labels from kallsyms, kdb_bb() works +backwards until it finds a non-spurious label then works forwards to the next +non-spurious label. That gives a real start and end address and a real name for +the current function. + +Note that this algorithm only applies in kdb_bb() when it maps rip to a suitable +start and end address. When disassembling the code, you will still see the +spurious label names, users need to see the extra labels. ret_from_exception on +i386 disassembles like this (2.6.22) :- + +[0]kdb> id ret_from_exception +0xc0102554 ret_from_exception: cli +0xc0102555 ret_from_intr: mov $0xfffff000,%ebp +0xc010255a ret_from_intr+0x5: and %esp,%ebp +0xc010255c check_userspace: mov 0x34(%esp),%eax +0xc0102560 check_userspace+0x4: mov 0x30(%esp),%al +0xc0102564 check_userspace+0x8: and $0x20003,%eax +0xc0102569 check_userspace+0xd: cmp $0x3,%eax +0xc010256c check_userspace+0x10: jb 0xc010258c resume_kernel +0xc0102572 check_userspace+0x16: mov %esi,%esi +0xc0102574 resume_userspace: cli +0xc0102575 resume_userspace+0x1: mov 0x8(%ebp),%ecx +0xc0102578 resume_userspace+0x4: and $0xfe3e,%ecx +0xc010257e resume_userspace+0xa: jne 0xc01026f4 work_pending +0xc0102584 resume_userspace+0x10: jmp 0xc01026a7 restore_all +0xc0102589 resume_userspace+0x15: lea 0x0(%esi),%esi +0xc010258c resume_kernel: cli + +For the purposes of kdba_bt_x86.c, any rip from 0xc0102554 to 0xc0102589 needs +to map to the range 0xc0102554 (start) to 0xc010258c (end) with function name +ret_from_exception. Therefore ret_from_intr, check_userspace and +resume_userspace are listed in bb_spurious[] for i386 so those symbols are +ignored. The comments in bb_spurious[] list the function that encloses each +spurious label, those comments are only for humans, they do not affect the code. + +Once rip has been mapped to non-spurious labels, the module name, function name, +start and end address are stored in variables bb_mod_name, bb_func_name, +bb_func_start, bb_func_end. These variables are used throughout kdba_bt_x86.c +for processing each function in turn. + +Watch for changes to assembler code, especially in arch/i386/kernel/entry.S, +arch/x86_64/kernel/entry.S and arch/x86_64/ia32/ia32entry.S. When new labels +are added you may need to adjust bb_spurious[] for that architecture. Running +bb_all can help identify assembler labels that have been added or deleted. + + +Tracking the Effects of Instructions +==================================== + +bb_pass2_do_changed_blocks() uses the KDB disassembler to decode the x86 +instructions to something a human can read. bb_dis_pass2() is used as a print +routine to store data for a single instruction in a buffer then +bb_parse_buffer() starts the analysis. Any instruction prefixes like lock or +rep are stripped out. The opcode string is isolated then up to 3 operands are +extracted (imul can have 3 operands), these are src, dst and dst2. The operand +is matched against table bb_opcode_usage_all[] which lists all the instructions +that actually appear in i386 and x86_64 kernels. A lot of the x86 instrcution +set is not used by the kernel so instructions such as SSE do not appear in +bb_opcode_usage_all[]. + +Each operand is decoded by bb_parse_operand() to see whether it has a segment +prefix, displacement, base, index or scale. An indirect call or jmp is +identified. Operands consisting only of a register are classified as 'reg' +type, displacements starting with '$' are immediate values otherwise the operand +refers to a memory location. Any base or index register name is mapped to the +abstract register name that contains it, this takes care of mapping (say) %ah to +rax. + +After decoding the opcode and all its operands, bb_usage() decides what effect +the instruction will have on the abstract state machine. Some x86 instructions +list all the affected registers in their operands and these can be handled as +generic cases. Alas many x86 instructions have side effects and change +registers that are not listed in the operands, these have to be handled as +special cases. enum bb_operand_usage lists the generic and special cases. + +bb_usage() is basically one huge switch statement over the special values in +enum bb_operand_usage. For each special case it tracks the side effects of the +instruction. Once all the special cases have been handled and converted to +generic cases then bb_usage() handles the generic cases. + +bb_usage() detects when a register is copied to another register, a register is +copied to stack or a known stack value is copied to a register and updates the +state data accordingly. It is particularly important that all stack pointer +updates and copies of the stack pointer are tracked, much of the saved state is +on stack and can be accessed via any register that points to the stack, not just +via rsp. + +i386 built with 4K stacks and all x86_64 builds have multiple kernel stacks. +bb_usage() knows which instructions or locations are used to switch stacks and +pretends that these instructions have no effect on the contents of rsp. The +higher level backtrace code knows how to handle stack switching, it is too +complicated for basic block analysis. + + +Transfer of Control Outside the Current Function +================================================ + +Ignoring call instructions, most C code does not transfer control outside the +current function, IOW there are no jump instructions to instructions outside the +function. There are a few cases that this can occur for C code, inline +assembler and tail recursion. + +Tail recursion occurs when a function ends by returning the value from a second +function and that second function has exactly the same arguments and return +value as the current function. For example, + + int bar(int i, char *p) + { + ... do some work and return an int ... + } + + int foo(int i, char *p) + { + return bar(i, p); + } + +If tail recursion is active (gcc -foptimize-sibling-calls) then instead of foo +calling bar, bar returning to foo then foo returning to its caller, gcc will end +foo with a direct jmp to bar. The source code says that something called foo +but the stack trace will show bar is active, with no sign of foo on stack. When +bar returns it will use the return address from the code that called foo. + +bb_transfer() detects an unconditional jmp to code outside the function body and +assumes that this represents tail recursion. For tail recursion to work +correctly, all the preserved registers must contain their original values, +bb_sanity_check() validates this. Any deviation from the expected state will +stop basic block analysis and fall back on the old unreliable backtrace code. + +Besides tail recursion in C code, assembler code can jump to labels outside the +current function. Unfortunately this occurs all the time in the twisty +assembler code and, to make things worse, many of these transfers are done with +non-standard register or memory state. bb_special_case() and the arch specific +bb_special_cases[] handle all the known special cases, including what the +register and/or memory state should be. Any deviation from the expected state +will stop basic block analysis and fall back on the old unreliable backtrace +code. + + +Locating Arguments +================== + +Function arguments can be passed in registers or on stack. The full ABI for +passing arguments is described in + + http://www.caldera.com/developers/devspecs/abi386-4.pdf + http://www.x86-64.org/documentation/abi.pdf + +The short description, ignoring special cases like passing structures by name +and floating point arguments which tend not to apply to the kernel, is :- + +i386. With -mpregparm=0, all arguments are passed on stack, except for + functions defined as FASTCALL, where the first 3 arguments are passed in + registers. + + With -mregparm=3, the first 3 arguments are passed in registers except + for functions defined as asmlinkage or with variable number of + arguments, when arguments are still passed on stack. -mpregparm=3 used + to be a config option, in recent kernels it is the default. + + Arguments defined as long long (64 bit) are passed in two registers or + in two locations on stack. Being passed in two pieces makes a 64 bit + argument look like two 32 bit arguments to KDB, it will be printed as + two separate arguments. + + When compiled with -mregparm=3, if a 64 bit argument is argument number + 2 then it will not be split between register and stack, instead it will + all be on stack and the third argument register will not be used. This + makes it look like there is an extra argument in the list. There is + nothing that KDB can do to detect these corner cases with 64 bit + arguments on i386, which is a pity because they can confuse users. + + The preserved registers are ebx, ebp, esp, esi, edi. Arguments are + passed in eax, edx, ecx. The return value is in eax. + +x86_64. The first 6 arguments are passed in registers, the 7th and later + arguments are passed on stack. Except for functions with a variable + number of arguments (e.g. printk) where all arguments are on stack + except for rax which contains the number of SSE arguments (always 0 for + the kernel). + + The preserved registers are rbx, rbp, rsp, r12, r13, r14, r15. + Arguments are passed in rdi, rsi, rdx, rcx, r8, r9. The return value is + in rax. + +For both architectures, kdba_bt detects an argument that is passed in a register +by the fact that the function code reads from that argument type register while +it contains its original value. IOW, testing the value of rax, copying rax to +another register or storing it on stack without first overwriting rax means that +rax contains a useful input value. Reading from memory which is above the +original stack pointer means that there is a argument at that location on +stack. + +There are some functions in the kernel whose definition contains arguments that +are not actually used. Typically these functions are instantiations of generic +function definitions where some, but not all, instantiations use all the +arguments. For example, a filesystem function may take flags that are not used +by this particular filesystem, but the function definition has to match the +generic filesystem declarations. If the unused arguments are at the end of the +list then there is no way of telling from the object code that they exist, the +function that does not use the trailing aguments will have no code that refers +to them. KDB will print a truncated argument list for this case. + +If the unused arguments are not at the end of the list then KDB can detect the +presence of the unused arguments, because there is code that refers to later +arguments. KDB will print the unused argument, although gcc may have +overwritten the register it is in, in which case KDB prints "invalid". + +Originally kdba_bt_x86 would detect that there was no reference to arguments in +registers but there were still references to arguments on stack and assume that +the function had all its arguments on stack. Unfortunately this did not work +with the large number of 'pass through' functions in the kernel. A 'pass +through' function is one which calls another function with roughly the same +argument list and makes no other reference to the register arguments. For +example, ipv4_doint_and_flush_strategy() takes 7 arguments, calls +devinet_conf_sysctl() with those 7 arguments in the same order and has no other +reference to any of its arguments. + +Pass through functions do not touch the arguments that are passed in registers +because they are already in the right location for the routine about to be +called, so the pass through function has no code that references the argument +registers. No code means that kdba_bt_x86 cannot tell if the function has +register arguments or not. The arguments passed on stack must always be copied +to the new stack frame, even for pass through functions, so the arguments on +stack can always be detected. + +kdba_bt_x86 was changed to assume that if there are any arguments on stack then +there are always arguments in registers, except for a list of functions that are +known to be asmlinkage or to have a variable number of arguments. +bb_assume_pass_through() ignores the known special cases, for other functions +which have stack arguments but no register arguments it assumes the function is +pass through and prints a warning about that assumption. + +The above heuristics mean that there is one case that kdba_bt_x86 cannot detect: +pass through functions where all the arguments are in registers. These have no +argument references at all in their code, so they are printed with no arguments. +All they do is call another function so this class of functions never fails, or +if it does fail then it is due to something that is not argument related. If +the failure is further down the call stack then the arguments are printed at the +next function down the stack, so the user still has the arguments. + +This list of limitations on getting the x86 arguments may seem to be a long one, +but kdba_bt_x86 gives sensible results for most functions. For kernel +debugging, any arguments are far better than none at all. + + +Kernel Stack Switching +====================== + +Understanding the unusual way that x86 kernel stacks are used is very important +when diagnosing backtrace problems. Every process has its own normal kernel +stack, even processes that run entirely within the kernel such as kthread or the +per cpu migration processes. The normal stacks are 4K or 8K on i386 (depending +on CONFIG_4KSTACKS) and 8K on x86_64. The normal stacks are global, they are +not tied to any cpu. + +For i386 with 8K stacks there are no other kernel stacks so there is no stack +switching to worry about. + +For i386 with 4K process stacks, each cpu also has a 4K soft irq stack and a 4K +hard irq stack. It is possible for a process to be running on its own process +stack, for the process to be interrupted by a soft irq which is then interrupted +by a hard irq. At that point the backtrace is split between the hard irq, the +soft irq and the normal normal stacks. + +On x86_64, each cpu always has stacks for stackfault, doublefault, nmi, debug, +mce and interrupts. See Documentation/x86_64/kernel-stacks. + +The arch specific kdba_get_stack_info_alternate() function works out which stack +the backtrace starts on, how big the stack is and how to switch to the next +stack. This information is stored in the kdb_activation_record and used by the +higher level backtrace code to detect a stack switch. + +The normal stack has some padding at the end, this reflects the stack pointer +when the process was created in the kernel. kdba_bt_x86 cannot backtrace +through this padding data, mainly because the code that set the nitial stack +pointer no longer exists after boot. ARCH_NORMAL_PADDING defines how many words +to ignore at the end of the normal stack. + + +Debugging KDB +============= + +KDB has conditional debugging print statements scattered throughout the code. +If KDB is not behaving as expected, you can turn on debugging and rerun the +command. Before debugging KDB, set LINES 10000 and capture the output via a +serial console. If using minicom, turn on word wrap (control-A W) and capture +mode (control-A L). If you are using a serial console via a serial to Ethernet +interface using ssh or telnet, use the 'script' command to start the session. + +The various KDB_DEBUG_FLAG_* flags are listed in include/linux/kdbprivate.h. +You set debug with 'set KDBDEBUG 0xnn' where nn is the or'd value of the desired +flags. 'set KDBDEBUG 0' turns off KDB debugging. When diagnosing x86 backtrace +problems, the most useful debug flags are + + KDB_DEBUG_FLAG_ARA 0x10 Activation record, arch specific + KDB_DEBUG_FLAG_BB_SUMM 0x04 Basic block analysis, summary only + KDB_DEBUG_FLAG_BB 0x20 All basic block analysis + +ARA prints information about the different kernel stacks as kdba_bt_x86 unwinds +through the switched kernel stacks. BB_SUMM prints a summary of the basic block +analysis for each function, including the abstract exit state and the rollback +calculations. BB prints a huge amount of basic block debugging, you probably +only want to turn this for the full backtrace on as a last resort. + +I find 'set KDBDEBUG 0x14' to be best to get an overview of a problem. It gives +both the kernel stack information plus the abstract state and actual location of +data for each function. + +Command 'bb1' does a detailed debug session for a single function, bb1 takes a +single parameter, the address of the exit point from the function, by number, +not by name. bb1 turns on KDB_DEBUG_FLAG_BB, does basic block analysis for the +function that contains the exit point then resets the debug flags to their +previous value. + +Command 'bb_all' runs through every function in the base kernel (not module +functions) and does a basic block analysis of every function. It also validates +the various tables in kdba_bt_x86 where possible. bb_all is meant for the KDB +maintainer to check that all the base kernel function pass the sanity checks, it +can also be used by end users when reporting a bug. bb_all takes no parameters. +It prints a '.' for every 100 functions it has analysed and allows for up to 20 +errors before giving up. The output from bb_all also includes the config +variables that affect basic block analysis plus any assumptions about 'pass +through' functions. + + +Submitting a Bug Report Against kdba_bt_x86 +=========================================== + +Capture the KDB output via a serial console. + +set LINES 10000 +set BTSP 1 +set KDBDEBUG 0x14 +Reproduce the problem. +set KDBDEBUG 0 +bb_all + +If you can identify the rip/eip where kdba_bt_x86 gets confused, run bb1 with +that address. + +Find each set of output from kdba_get_stack_info in the trace, extract the last +two lines and type those lines into KDB. That will give a hex and symbolic dump +of the raw kernel stacks. For example, if the trace data is + +kdba_get_stack_info: esp=0xc04fbef8 cpu=0 task=c047b3e0 +kdba_get_stack_info: ar->stack + physical_start=0xc04fb000 + physical_end=0xc04fc000 + logical_start=0xc04fb038 + logical_end=0xc04fc000 + next=0xc04b4f44 + id=hardirq_ctx + set MDCOUNT 1024 + mds 0xc04fb000 + +then type the last two lines into KDB. Repeat this for each stack listed by +kdba_get_stack_info on the failing backtrace. + +Send all the console output to the KDB maintainer. + + +Examples of Basic Block Debugging Output +======================================== + +Example of the basic block analysis of fs/namei::getname() on i386. Kernel +2.6.22, i386, compiled with frame pointers, gcc 4.1.0. + +Basic block debugging is very verbose, so set a high number of output lines. +You really need a reliable serial console to capture this amount of output. + + [0]kdb> set LINES 10000 + +A simple disassemble of getname(). This is not required for debugging purposes +since each instruction is printed as part of basic block debugging, but this can +be easier to read. + + [0]kdb> id getname + 0xc015cce8 getname: push %ebp + 0xc015cce9 getname+0x1: mov %esp,%ebp + 0xc015cceb getname+0x3: push %edi + 0xc015ccec getname+0x4: push %esi + 0xc015cced getname+0x5: push %ebx + 0xc015ccee getname+0x6: sub $0x4,%esp + 0xc015ccf1 getname+0x9: mov %eax,%edi + 0xc015ccf3 getname+0xb: mov $0xd0,%edx + 0xc015ccf8 getname+0x10: mov 0xc04b2120,%eax + 0xc015ccfd getname+0x15: call 0xc0153009 kmem_cache_alloc + 0xc015cd02 getname+0x1a: mov %eax,0xfffffff0(%ebp) + 0xc015cd05 getname+0x1d: mov $0xfffffff4,%eax + 0xc015cd0a getname+0x22: cmpl $0x0,0xfffffff0(%ebp) + 0xc015cd0e getname+0x26: je 0xc015cd7d getname+0x95 + 0xc015cd10 getname+0x28: mov %esp,%eax + 0xc015cd12 getname+0x2a: and $0xfffff000,%eax + 0xc015cd17 getname+0x2f: cmpl $0xffffffff,0x18(%eax) + 0xc015cd1b getname+0x33: je 0xc015cd39 getname+0x51 + 0xc015cd1d getname+0x35: mov $0xfffffff2,%esi + 0xc015cd22 getname+0x3a: cmp $0xbfffffff,%edi + 0xc015cd28 getname+0x40: ja 0xc015cd60 getname+0x78 + 0xc015cd2a getname+0x42: mov $0xc0000000,%ebx + 0xc015cd2f getname+0x47: sub %edi,%ebx + 0xc015cd31 getname+0x49: cmp $0xfff,%ebx + 0xc015cd37 getname+0x4f: jbe 0xc015cd3e getname+0x56 + 0xc015cd39 getname+0x51: mov $0x1000,%ebx + 0xc015cd3e getname+0x56: mov %ebx,%ecx + 0xc015cd40 getname+0x58: mov %edi,%edx + 0xc015cd42 getname+0x5a: mov 0xfffffff0(%ebp),%eax + 0xc015cd45 getname+0x5d: call 0xc023dbb4 strncpy_from_user + 0xc015cd4a getname+0x62: cmp $0x0,%eax + 0xc015cd4d getname+0x65: jle 0xc015cd5a getname+0x72 + 0xc015cd4f getname+0x67: mov $0xffffffdc,%esi + 0xc015cd54 getname+0x6c: cmp %ebx,%eax + 0xc015cd56 getname+0x6e: jae 0xc015cd60 getname+0x78 + 0xc015cd58 getname+0x70: jmp 0xc015cd71 getname+0x89 + 0xc015cd5a getname+0x72: je 0xc015cd76 getname+0x8e + 0xc015cd5c getname+0x74: jge 0xc015cd71 getname+0x89 + 0xc015cd5e getname+0x76: mov %eax,%esi + 0xc015cd60 getname+0x78: mov 0xfffffff0(%ebp),%edx + 0xc015cd63 getname+0x7b: mov 0xc04b2120,%eax + 0xc015cd68 getname+0x80: call 0xc01521f1 kmem_cache_free + 0xc015cd6d getname+0x85: mov %esi,%eax + 0xc015cd6f getname+0x87: jmp 0xc015cd7d getname+0x95 + 0xc015cd71 getname+0x89: mov 0xfffffff0(%ebp),%eax + 0xc015cd74 getname+0x8c: jmp 0xc015cd7d getname+0x95 + 0xc015cd76 getname+0x8e: mov $0xfffffffe,%esi + 0xc015cd7b getname+0x93: jmp 0xc015cd60 getname+0x78 + 0xc015cd7d getname+0x95: pop %edx + 0xc015cd7e getname+0x96: pop %ebx + 0xc015cd7f getname+0x97: pop %esi + 0xc015cd80 getname+0x98: pop %edi + 0xc015cd81 getname+0x99: pop %ebp + 0xc015cd82 getname+0x9a: ret + +The bb1 command only one argument which must be an address, not a name. bb1 +turns on full basic block debugging and analyses the function containing the +supplied address. Give bb1 the address of the exit point from this function, +IOW the return address that is stored on stack due to a call from this function +to the next function down the call stack. Assume that getname() has called +kmem_cache_free() and something went wrong in kmem_cache_free() or one of the +functions that it calls. The call to kmem_cache_free is at 0xc015cd68 and the +return address on stack is the instruction after the call, i.e. 0xc015cd6d, so + + [0]kdb> bb1 0xc015cd6d + bb_pass1: func_name getname func_start 0xc015cce8 func_end 0xc015cd83 + +bb_pass1 has identified the function name and its start and end address. For C +functions these are just the function start address and the next symbol in +kallsyms. For Assembler code there may be spurious labels so the function name +may not match the label prior to the address given to bb1. For an example of +that on i386, find the address of resume_userspace then pass that address to the +bb1 KDB command. + + bb_pass1: end + bb[0] start 0xc015cce8 end 0xc015cd38 drop_through 1 + bb[1] start 0xc015cd39 end 0xc015cd3d drop_through 1 + bb[2] start 0xc015cd3e end 0xc015cd58 drop_through 0 + bb[3] start 0xc015cd5a end 0xc015cd5f drop_through 1 + bb[4] start 0xc015cd60 end 0xc015cd6f drop_through 0 + bb[5] start 0xc015cd71 end 0xc015cd74 drop_through 0 + bb[6] start 0xc015cd76 end 0xc015cd7b drop_through 0 + bb[7] start 0xc015cd7d end 0xc015cd82 drop_through 0 + bb_jmp[0] from 0xc015cd0e to 0xc015cd7d drop_through 0 + bb_jmp[1] from 0xc015cd1b to 0xc015cd39 drop_through 0 + bb_jmp[2] from 0xc015cd28 to 0xc015cd60 drop_through 0 + bb_jmp[3] from 0xc015cd37 to 0xc015cd3e drop_through 0 + bb_jmp[4] from 0xc015cd4d to 0xc015cd5a drop_through 0 + bb_jmp[5] from 0xc015cd56 to 0xc015cd60 drop_through 0 + bb_jmp[6] from 0xc015cd58 to 0xc015cd71 drop_through 0 + bb_jmp[7] from 0xc015cd5a to 0xc015cd76 drop_through 0 + bb_jmp[8] from 0xc015cd5c to 0xc015cd71 drop_through 0 + bb_jmp[9] from 0xc015cd6f to 0xc015cd7d drop_through 0 + bb_jmp[10] from 0xc015cd74 to 0xc015cd7d drop_through 0 + bb_jmp[11] from 0xc015cd7b to 0xc015cd60 drop_through 0 + bb_jmp[12] from 0xc015cd38 to 0xc015cd39 drop_through 1 + bb_jmp[13] from 0xc015cd3d to 0xc015cd3e drop_through 1 + bb_jmp[14] from 0xc015cd5f to 0xc015cd60 drop_through 1 + +After analysing the logic flow, we can see that getname() consists of 8 basic +blocks (nodes in bb_list[]). 5 of these blocks end in unconditional jumps, the +other 3 drop through to the next block. There are 15 transfers of control +(vertices in bb_jmp_list[]). 12 of these transfers are explicit jmp or jcc +instructions, the other 3 are implicit transfers when dropping through from one +block to the next. The node list is sorted by start address, the vertex list is +not sorted. + +Basic block 0 starts at the function start (0xc015cce8) and ends at 0xc015cd38. +0xc015cd39 is the target of a jump instruction (0xc015cd1b: je 0xc015cd39) so +0xc015cd39 starts a new block, which means that 0xc015cd38 becomes the end of +the previous block. Because bb[0] does not end in an explicit jmp instruction, +there is a drop through from the end of bb[0] to the start of bb[1], see +bb_jmp[12]. + + bb_pass2: start + +To get the most accurate results from pass2, try to scan the directed graph by +only looking at nodes whose inputs are all defined. Initially only process +nodes with no missing inputs. + + bb_pass2_do_changed_blocks: allow_missing 0 + + bb[0] + bb_reg_state c07282e0 + rax = rax + rbx = rbx + rcx = rcx + rdx = rdx + rdi = rdi + rsi = rsi + rbp = rbp + rsp = osp+0x0 + +The initial state for bb[0] is the same for all C functions. Each register +contains its own abstract value, except for rsp which is defined in terms of the +original stack pointer (osp). + + '0xc015cce8 getname: push %ebp' + +The first instruction of getname() saves the frame pointer. + + opcode 'push' matched by 'push', usage 44 + src R: %ebp base_rc 8 (rbp) + +bb_usage() reports how the instruction was recognised and how its operands were +decoded. Although this is i386 (ebp), it is reported as rbp. Using the x86_64 +names for registers throughout makes it easier to create common code for the two +architecures. + + rsp osp offset +0x0 -> -0x4 + +A push instruction decrements rsp by 4 (i386) or 8 (x86_64) bytes. rsp +originally contained the original stack pointer (osp), now it contains the +original stack pointer - 4. + + *(rsp+0x0 osp-0x4) = rbp slot 0 + +The stack location pointed to by *rsp now contains the original value of rbp. +Since rsp contains (osp-0x4), *(osp-0x4) contains rbp. It is slot 0 in the +memory array associated with the register state. + + '0xc015cce9 getname+0x1: mov %esp,%ebp' + opcode 'mov' matched by 'mov', usage 36 + src R: %esp base_rc 9 (rsp) + dst R: %ebp base_rc 8 (rbp) + rbp = rsp (osp-0x4) + +Copy esp (rsp) to ebp (rbp). rsp contained (osp-0x4) so rbp also contains +(osp-0x4). Any reference to data via either rbp or rsp will now be tracked as a +stack location. + + '0xc015cceb getname+0x3: push %edi' + opcode 'push' matched by 'push', usage 44 + src R: %edi base_rc 6 (rdi) + rsp osp offset -0x4 -> -0x8 + *(rsp+0x0 osp-0x8) = rdi slot 1 + '0xc015ccec getname+0x4: push %esi' + opcode 'push' matched by 'push', usage 44 + src R: %esi base_rc 7 (rsi) + rsp osp offset -0x8 -> -0xc + *(rsp+0x0 osp-0xc) = rsi slot 2 + '0xc015cced getname+0x5: push %ebx' + opcode 'push' matched by 'push', usage 44 + src R: %ebx base_rc 3 (rbx) + rsp osp offset -0xc -> -0x10 + *(rsp+0x0 osp-0x10) = rbx slot 3 + +Push 3 registers to stack. rsp is adjusted for each push and stack locations +are assigned to contain the values of edi, esi and ebx. This sequence is very +common in i386 C functions. edi, esi and ebx are preserved registers on i386, +but gcc wants to use them for scratch space. The original contents iof these +registers must be saved on stack and restored before returning to our caller. + + '0xc015ccee getname+0x6: sub $0x4,%esp' + opcode 'sub' matched by 'sub', usage 51 + src I: $0x4 + dst R: %esp base_rc 9 (rsp) + rsp osp offset -0x10 -> -0x14 + +Subtract 4 bytes from esp. This defines the local stack variables. Sorry, +names for local stack variables are not available to KDB. + + '0xc015ccf1 getname+0x9: mov %eax,%edi' + opcode 'mov' matched by 'mov', usage 36 + src R: %eax base_rc 2 (rax) + dst R: %edi base_rc 6 (rdi) + rdi = rax (rax) + +Having saved edi on stack, gcc now overwrites edi with eax. At this point rax +still contains its original value, so rdi now contains a copy of rax, as well as +the original value which is still in rax. This is a common sequence in C code. +rax contains argument 0 but it is also a scratch register. If the code needs to +use argument 0 later then its value must be saved somewhere before executing any +instruction that changes rax. edi is a preserved register so its contents will +not be changed by any function that we call, or if it is changed then it will be +restored before returning to this function. + +rax is listed in the arch specific bb_param_reg[] list and the code is reading +from rax while it still contains its original value. The only way that makes +any sense is when rax is an input argument to getname(). We note that fact in +bb_reg_read(). + + '0xc015ccf3 getname+0xb: mov $0xd0,%edx' + opcode 'mov' matched by 'mov', usage 36 + src I: $0xd0 + dst R: %edx base_rc 5 (rdx) + rdx = undefined + +Moving an constant value to edx. Although this is a constant, it does not refer +to any of the original values that were supplied to this function. Therefore +rdx becomes undefined for the purposes of the code analysis. + + '0xc015ccf8 getname+0x10: mov 0xc04b2120,%eax' + opcode 'mov' matched by 'mov', usage 36 + src M: 0xc04b2120 + dst R: %eax base_rc 2 (rax) + rax = undefined + +Moving a constant value to eax makes rax undefined. + + '0xc015ccfd getname+0x15: call 0xc0153009 ' + opcode 'call' matched by 'call', usage 17 + src M: 0xc0153009 + bb_reg_state c0728658 + rax = undefined + rbx = rbx + rcx = rcx + rdx = undefined + rdi = rax + rsi = rsi + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + +Basic block debugging prints the register and memory state when transfering +control between blocks and when issuing call instructions. The call state is +mainly useful when C code calls assembler routines, especially if you are not +sure what state the assembler code expects. Not all of our assembler is as well +documented as it could be :( + + rax = undefined + rcx = undefined + rdx = undefined + +The i386 ABI says that some registers are preserved across calls, see the arch +specific bb_preserved_reg[] list. Any registers not in that list automatically +become undefined after a call instruction. + + '0xc015cd02 getname+0x1a: mov %eax,0xfffffff0(%ebp)' + opcode 'mov' matched by 'mov', usage 36 + src R: %eax base_rc 2 (rax) + dst M: 0xfffffff0(%ebp) base_rc 8 (rbp) + +eax is the return value from the call, it is being saved at offset 0xfffffff0 +(-0x10) from ebp. Since rbp contains (osp-0x4) the return value is being stored +at (osp-0x14). This is a stack location but we have no record of any data being +held at that location, it is part of the local stack variables. + + '0xc015cd05 getname+0x1d: mov $0xfffffff4,%eax' + opcode 'mov' matched by 'mov', usage 36 + src I: $0xfffffff4 + dst R: %eax base_rc 2 (rax) + rax = undefined + '0xc015cd0a getname+0x22: cmpl $0x0,0xfffffff0(%ebp)' + opcode 'cmpl' matched by 'cmp', usage 3 + src I: $0x0 + dst M: 0xfffffff0(%ebp) base_rc 8 (rbp) + '0xc015cd0e getname+0x26: je 0xc015cd7d ' + opcode 'je' matched by 'j', usage 28 + src M: 0xc015cd7d + bb_reg_state c0728658 + rax = undefined + rbx = rbx + rcx = undefined + rdx = undefined + rdi = rax + rsi = rsi + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + +Transfer of control, print the register and memory state. + + matched: from 0xc015cd0e to 0xc015cd7d drop_through 0 bb_jmp[0] + +Which bb_jmp_list[] entry matches this transfer. + + new state c07286b8 + +The current abstract register and memory state is cloned at address c07286b8. +This state becomes one of the inputs to the basic block whose start address is +0xc015cd7d. + + '0xc015cd10 getname+0x28: mov %esp,%eax' + opcode 'mov' matched by 'mov', usage 36 + src R: %esp base_rc 9 (rsp) + dst R: %eax base_rc 2 (rax) + rax = rsp (osp-0x14) + +Copy rsp which contains (osp-0x14) to rax. rax contains a valid stack pointer. + + '0xc015cd12 getname+0x2a: and $0xfffff000,%eax' + opcode 'and' matched by 'and', usage 11 + src I: $0xfffff000 + dst R: %eax base_rc 2 (rax) + rax = undefined + +But not for long. + + '0xc015cd17 getname+0x2f: cmpl $0xffffffff,0x18(%eax)' + opcode 'cmpl' matched by 'cmp', usage 3 + src I: $0xffffffff + dst M: 0x18(%eax) base_rc 2 (rax) + '0xc015cd1b getname+0x33: je 0xc015cd39 ' + opcode 'je' matched by 'j', usage 28 + src M: 0xc015cd39 + bb_reg_state c0728658 + rax = undefined + rbx = rbx + rcx = undefined + rdx = undefined + rdi = rax + rsi = rsi + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + +Another transfer of control, print the state. + + matched: from 0xc015cd1b to 0xc015cd39 drop_through 0 bb_jmp[1] + +Which bb_jmp_list[] entry was used. + + reuse bb_jmp[0] + +To save space, we only clone the state if it is different. Otherwise we reuse +the state from another vertex and bump the reference count. + + '0xc015cd1d getname+0x35: mov $0xfffffff2,%esi' + opcode 'mov' matched by 'mov', usage 36 + src I: $0xfffffff2 + dst R: %esi base_rc 7 (rsi) + rsi = undefined + +Using esi as a scratch register, even though the i386 ABi says that esi is a +preserved register. Not to worry, the original value of rsi was saved on stack +on entry and it will be restored before exit. + + '0xc015cd22 getname+0x3a: cmp $0xbfffffff,%edi' + opcode 'cmp' matched by 'cmp', usage 3 + src I: $0xbfffffff + dst R: %edi base_rc 6 (rdi) + '0xc015cd28 getname+0x40: ja 0xc015cd60 ' + opcode 'ja' matched by 'j', usage 28 + src M: 0xc015cd60 + bb_reg_state c0728658 + rax = undefined + rbx = rbx + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + matched: from 0xc015cd28 to 0xc015cd60 drop_through 0 bb_jmp[2] + new state c0728710 + +This state is different from any states already saved, clone to a new entry. + + '0xc015cd2a getname+0x42: mov $0xc0000000,%ebx' + opcode 'mov' matched by 'mov', usage 36 + src I: $0xc0000000 + dst R: %ebx base_rc 3 (rbx) + rbx = undefined + '0xc015cd2f getname+0x47: sub %edi,%ebx' + opcode 'sub' matched by 'sub', usage 51 + src R: %edi base_rc 6 (rdi) + dst R: %ebx base_rc 3 (rbx) + rbx = undefined + '0xc015cd31 getname+0x49: cmp $0xfff,%ebx' + opcode 'cmp' matched by 'cmp', usage 3 + src I: $0xfff + dst R: %ebx base_rc 3 (rbx) + '0xc015cd37 getname+0x4f: jbe 0xc015cd3e ' + opcode 'jbe' matched by 'j', usage 28 + src M: 0xc015cd3e + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + matched: from 0xc015cd37 to 0xc015cd3e drop_through 0 bb_jmp[3] + new state c0728768 + +This state is different from any states already saved, clone to a new entry. + + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + matched: from 0xc015cd38 to 0xc015cd39 drop_through 1 bb_jmp[12] + reuse bb_jmp[3] + +Basic block 0 drops through to basic block 1, treat it as an implicit transfer +of control. The state is the same as the previous jump instruction so reuse it +and bump the reference count. + +That ends basic block 0, now pick the next block in the list that (a) needs to +be scanned and (b) has all its input states defined. In this case bb[1]. + + bb[1] + +bb[1] starts at 0xc015cd39 and has two paths that transfer control to it. +bb_jmp[1] from an explicit jump at 0xc015cd1b and a drop through at bb_jmp[12]. +Where there is more than one input state we have to merge them and reconcile the +final value. + + first state c07286b8 + +The first input state is stored at c07286b8. Looking back through the trace we +find that entry associated with bb_jmp[0], not bb_jmp[1] as expected. However +bb_jmp[1] reused the state that was stored for bb_jmp[0] so all is well. + + bb_reg_state c0728658 + rax = undefined + rbx = rbx + rcx = undefined + rdx = undefined + rdi = rax + rsi = rsi + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + +The first state for bb[1]. + + merging state c0728768 + +Now merge the second state, which is held at c0728768. + + rbx = undefined + rsi = undefined + +The two states disagree on the values being tracked in rbx and rsi. Compiler +theory 101 says that if two or more paths to a basic block have different values +for a register then that register cannot be relied on at the start of the block, +so make it undefined. The same logic applies to memory locations. + + final state + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + +After merging all the input states, this is the final starting state for bb[1]. +Now track what bb[1] does to the state. + + '0xc015cd39 getname+0x51: mov $0x1000,%ebx' + opcode 'mov' matched by 'mov', usage 36 + src I: $0x1000 + dst R: %ebx base_rc 3 (rbx) + rbx = undefined + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + matched: from 0xc015cd3d to 0xc015cd3e drop_through 1 bb_jmp[13] + reuse bb_jmp[3] + +bb[1] is a single instruction which drops through to bb[2]. + + bb[2] + first state c0728768 + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + merging state c0728768 + +bb[2] has two inputs, both vertices are pointing to input state c0728768. +Merging an entry with itself has no effect. + + '0xc015cd3e getname+0x56: mov %ebx,%ecx' + opcode 'mov' matched by 'mov', usage 36 + src R: %ebx base_rc 3 (rbx) + dst R: %ecx base_rc 4 (rcx) + rcx = rbx (undefined) + '0xc015cd40 getname+0x58: mov %edi,%edx' + opcode 'mov' matched by 'mov', usage 36 + src R: %edi base_rc 6 (rdi) + dst R: %edx base_rc 5 (rdx) + rdx = rdi (rax) + '0xc015cd42 getname+0x5a: mov 0xfffffff0(%ebp),%eax' + opcode 'mov' matched by 'mov', usage 36 + src M: 0xfffffff0(%ebp) base_rc 8 (rbp) + dst R: %eax base_rc 2 (rax) + rax = *(rbp-0x10) (osp-0x14) rax = undefined + '0xc015cd45 getname+0x5d: call 0xc023dbb4 ' + opcode 'call' matched by 'call', usage 17 + src M: 0xc023dbb4 + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = rax + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + rax = undefined + rcx = undefined + rdx = undefined + '0xc015cd4a getname+0x62: cmp $0x0,%eax' + opcode 'cmp' matched by 'cmp', usage 3 + src I: $0x0 + dst R: %eax base_rc 2 (rax) + '0xc015cd4d getname+0x65: jle 0xc015cd5a ' + opcode 'jle' matched by 'j', usage 28 + src M: 0xc015cd5a + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + matched: from 0xc015cd4d to 0xc015cd5a drop_through 0 bb_jmp[4] + reuse bb_jmp[3] + '0xc015cd4f getname+0x67: mov $0xffffffdc,%esi' + opcode 'mov' matched by 'mov', usage 36 + src I: $0xffffffdc + dst R: %esi base_rc 7 (rsi) + rsi = undefined + '0xc015cd54 getname+0x6c: cmp %ebx,%eax' + opcode 'cmp' matched by 'cmp', usage 3 + src R: %ebx base_rc 3 (rbx) + dst R: %eax base_rc 2 (rax) + '0xc015cd56 getname+0x6e: jae 0xc015cd60 ' + opcode 'jae' matched by 'j', usage 28 + src M: 0xc015cd60 + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + matched: from 0xc015cd56 to 0xc015cd60 drop_through 0 bb_jmp[5] + reuse bb_jmp[3] + '0xc015cd58 getname+0x70: jmp 0xc015cd71 ' + opcode 'jmp' matched by 'j', usage 28 + src M: 0xc015cd71 + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + matched: from 0xc015cd58 to 0xc015cd71 drop_through 0 bb_jmp[6] + reuse bb_jmp[3] + + bb[3] + first state c0728768 + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + +bb[3] only has one input, nothing to merge. + + '0xc015cd5a getname+0x72: je 0xc015cd76 ' + opcode 'je' matched by 'j', usage 28 + src M: 0xc015cd76 + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + matched: from 0xc015cd5a to 0xc015cd76 drop_through 0 bb_jmp[7] + reuse bb_jmp[3] + '0xc015cd5c getname+0x74: jge 0xc015cd71 ' + opcode 'jge' matched by 'j', usage 28 + src M: 0xc015cd71 + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + matched: from 0xc015cd5c to 0xc015cd71 drop_through 0 bb_jmp[8] + reuse bb_jmp[3] + '0xc015cd5e getname+0x76: mov %eax,%esi' + opcode 'mov' matched by 'mov', usage 36 + src R: %eax base_rc 2 (rax) + dst R: %esi base_rc 7 (rsi) + rsi = rax (undefined) + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + matched: from 0xc015cd5f to 0xc015cd60 drop_through 1 bb_jmp[14] + reuse bb_jmp[3] + + bb[5] + first state c0728768 + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + merging state c0728768 + '0xc015cd71 getname+0x89: mov 0xfffffff0(%ebp),%eax' + opcode 'mov' matched by 'mov', usage 36 + src M: 0xfffffff0(%ebp) base_rc 8 (rbp) + dst R: %eax base_rc 2 (rax) + rax = *(rbp-0x10) (osp-0x14) rax = undefined + '0xc015cd74 getname+0x8c: jmp 0xc015cd7d ' + opcode 'jmp' matched by 'j', usage 28 + src M: 0xc015cd7d + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + matched: from 0xc015cd74 to 0xc015cd7d drop_through 0 bb_jmp[10] + reuse bb_jmp[3] + + bb[6] + first state c0728768 + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + '0xc015cd76 getname+0x8e: mov $0xfffffffe,%esi' + opcode 'mov' matched by 'mov', usage 36 + src I: $0xfffffffe + dst R: %esi base_rc 7 (rsi) + rsi = undefined + '0xc015cd7b getname+0x93: jmp 0xc015cd60 ' + opcode 'jmp' matched by 'j', usage 28 + src M: 0xc015cd60 + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + matched: from 0xc015cd7b to 0xc015cd60 drop_through 0 bb_jmp[11] + reuse bb_jmp[3] + + bb[4] + first state c0728710 + bb_reg_state c0728658 + rax = undefined + rbx = rbx + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + merging state c0728768 + rbx = undefined + merging state c0728768 + merging state c0728768 + +bb[4] has 4 inputs, 3 of which have the same state. One one path (state +c0728710) rbx is defined, on the others (c0728768) rbx is undefined so the final +state has rbx as undefined. + + final state + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + '0xc015cd60 getname+0x78: mov 0xfffffff0(%ebp),%edx' + opcode 'mov' matched by 'mov', usage 36 + src M: 0xfffffff0(%ebp) base_rc 8 (rbp) + dst R: %edx base_rc 5 (rdx) + rdx = *(rbp-0x10) (osp-0x14) rdx = undefined + '0xc015cd63 getname+0x7b: mov 0xc04b2120,%eax' + opcode 'mov' matched by 'mov', usage 36 + src M: 0xc04b2120 + dst R: %eax base_rc 2 (rax) + rax = undefined + '0xc015cd68 getname+0x80: call 0xc01521f1 ' + opcode 'call' matched by 'call', usage 17 + src M: 0xc01521f1 + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + rax = undefined + rcx = undefined + rdx = undefined + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + '0xc015cd6d getname+0x85: mov %esi,%eax' + opcode 'mov' matched by 'mov', usage 36 + src R: %esi base_rc 7 (rsi) + dst R: %eax base_rc 2 (rax) + rax = rsi (undefined) + '0xc015cd6f getname+0x87: jmp 0xc015cd7d ' + opcode 'jmp' matched by 'j', usage 28 + src M: 0xc015cd7d + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + matched: from 0xc015cd6f to 0xc015cd7d drop_through 0 bb_jmp[9] + reuse bb_jmp[3] + + bb[7] + first state c07286b8 + bb_reg_state c0728658 + rax = undefined + rbx = rbx + rcx = undefined + rdx = undefined + rdi = rax + rsi = rsi + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + merging state c0728768 + rbx = undefined + rsi = undefined + merging state c0728768 + final state + bb_reg_state c0728658 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + '0xc015cd7d getname+0x95: pop %edx' + opcode 'pop' matched by 'pop', usage 42 + src R: %edx base_rc 5 (rdx) + rdx = *(rsp+0x0) (osp-0x14) rdx = undefined + rsp osp offset -0x14 -> -0x10 + +This instruction is a bit misleading. It looks like gcc is restoring a value +from the stack *(osp-0x14) to edx, but we have no record of any useful data +being stored at osp-0x14. In fact gcc is just reducing the stack pointer by 4 +bytes to reverse the effect of 0xc015ccee: sub $0x4,%esp, the value popped into +edx contains nothing useful. Why gcc does pop instead of add $0x4,%esp is a +puzzle, probably some micro optimization. + + '0xc015cd7e getname+0x96: pop %ebx' + opcode 'pop' matched by 'pop', usage 42 + src R: %ebx base_rc 3 (rbx) + rbx = *(rsp+0x0) (osp-0x10) value rbx + rsp osp offset -0x10 -> -0xc + delete rbx from osp-0x10 slot 3 + +This pop is doing something useful. It is restoring the original value of the +preserved register ebx from stack, reversing 0xc015cced: push %ebx. Note that +incrementing rsp from osp-0x10 to osp-0xc invalidates the data held in memory at +osp-0x10, so we delete our record of it. + + '0xc015cd7f getname+0x97: pop %esi' + opcode 'pop' matched by 'pop', usage 42 + src R: %esi base_rc 7 (rsi) + rsi = *(rsp+0x0) (osp-0xc) value rsi + rsp osp offset -0xc -> -0x8 + delete rsi from osp-0xc slot 2 + '0xc015cd80 getname+0x98: pop %edi' + opcode 'pop' matched by 'pop', usage 42 + src R: %edi base_rc 6 (rdi) + rdi = *(rsp+0x0) (osp-0x8) value rdi + rsp osp offset -0x8 -> -0x4 + delete rdi from osp-0x8 slot 1 + +Pop the other preserved registers, in reverse order to the push sequence at the +start. + + '0xc015cd81 getname+0x99: pop %ebp' + opcode 'pop' matched by 'pop', usage 42 + src R: %ebp base_rc 8 (rbp) + rbp = *(rsp+0x0) (osp-0x4) value rbp + rsp osp offset -0x4 -> +0x0 + delete rbp from osp-0x4 slot 0 + +Pop the previous frame pointer. + + '0xc015cd82 getname+0x9a: ret ' + opcode 'ret' matched by 'ret', usage 48 + +When a ret instruction is executed, all the preserved registers must be back to +their original value and the stack pointer must contain osp+0. +bb_sanity_check() will complain and abort the backtrace if this is not true. No +complaints here. + + bb_pass2: end bb_reg_params 1 bb_memory_params 0 + +We identified one argument passed in a register (the read of rax at 0xc015ccf1) +and no reference to memory locations above the stack frame. So we have one +argument being passed in a register and no arguments being passed on stack. +This matches + + char * getname(const char __user * filename) + + bb_pass2: bb_exit_state at 0xc015cd6d + bb_reg_state c07287c0 + rax = undefined + rbx = undefined + rcx = undefined + rdx = undefined + rdi = rax + rsi = undefined + rbp = osp-0x4 + rsp = osp-0x14 + slot 0 offset_address -0x4 rbp + slot 1 offset_address -0x8 rdi + slot 2 offset_address -0xc rsi + slot 3 offset_address -0x10 rbx + +We told bb1 that the exit address from this function is 0xc015cd6d. The +abstract state at this exit point was saved, it defines how we rollback the +actual register values from the next function down the stack (kmem_cache_free) +to get the actual register values on entry to this function (getname). See +bb_actual_rollback() which updates bb_actual[]. + +Looking at the exit state above, we see that rsp contains the abstracte value +osp-0x14. It is a given that we have the actual value of rsp after the call +from getname() to kmem_cache_free(), otherwise we would not have found the +return address on stack and we would not be analysing getname(). Adding 0x14 +(the delta from osp to rsp) to our current actual rsp gives us the actual value +of osp on entry to getname(). + +The main aim of doing all this work is to track the function arguments so we can +print them if possible. getname() only has one argument which was passed in +eax. According to the abstract exit state, the original value of rax is +currently in rdi, so by looking at the actual value of rdi from the next stack +frame down we are able to get the argument to getname(). + +It is not always possible to get register arguments, gcc will only preserve +input arguments as long as it needs them so there may be no saved copy of +arguments that are passed in register. In this case, bt_print_one() prints +"invalid". + +If basic block analysis detected any arguments were passed on stack, their +contents can now be extracted based on the known value of the stack pointer. +bt_print_one() prints the arguments, if BT_ARGS is non-zero then any argument +that might be a kernel address is printed as a symbol. + +Once rsp has been rolled back to osp, we can calculate that actual address of +the stack locations that contain useful data. The previous values of rbp, rdi, +rsi and rbx are then copied from those locations into bb_actual[]. That gives +the values for those registers at the exit point from the function that called +getname(). Go up one level and repeat the analysis. + +There are two references to rdi in the exit state, which can be confusing. + + rdi = rax + slot 1 offset_address -0x8 rdi + +The first reference says that "register rdi contains the original value of rax", +the second reference says that "*(osp-0x8) contains the original value of rdi". +Do not confuse the two, one is by name, the other is by value. + +getname() is a fairly simple function, it has no loops. __follow_mount is more +complicated, it has loops as well as BUG() statements. + + [0]kdb> id __follow_mount + 0xc015be76 __follow_mount: push %ebp + 0xc015be77 __follow_mount+0x1: mov %esp,%ebp + 0xc015be79 __follow_mount+0x3: push %edi + 0xc015be7a __follow_mount+0x4: push %esi + 0xc015be7b __follow_mount+0x5: push %ebx + 0xc015be7c __follow_mount+0x6: mov %eax,%esi + 0xc015be7e __follow_mount+0x8: xor %edi,%edi + 0xc015be80 __follow_mount+0xa: jmp 0xc015beca __follow_mount+0x54 + 0xc015be82 __follow_mount+0xc: mov (%esi),%eax + 0xc015be84 __follow_mount+0xe: call 0xc0169664 lookup_mnt + 0xc015be89 __follow_mount+0x13: mov %eax,%ebx + 0xc015be8b __follow_mount+0x15: test %eax,%eax + 0xc015be8d __follow_mount+0x17: je 0xc015bed3 __follow_mount+0x5d + 0xc015be8f __follow_mount+0x19: mov 0x4(%esi),%eax + 0xc015be92 __follow_mount+0x1c: call 0xc0163de2 dput + 0xc015be97 __follow_mount+0x21: test %edi,%edi + 0xc015be99 __follow_mount+0x23: je 0xc015bead __follow_mount+0x37 + 0xc015be9b __follow_mount+0x25: mov (%esi),%eax + 0xc015be9d __follow_mount+0x27: test %eax,%eax + 0xc015be9f __follow_mount+0x29: je 0xc015bead __follow_mount+0x37 + 0xc015bea1 __follow_mount+0x2b: movl $0x0,0x64(%eax) + 0xc015bea8 __follow_mount+0x32: call 0xc016835b mntput_no_expire + 0xc015bead __follow_mount+0x37: mov %ebx,(%esi) + 0xc015beaf __follow_mount+0x39: mov 0x10(%ebx),%eax + 0xc015beb2 __follow_mount+0x3c: test %eax,%eax + 0xc015beb4 __follow_mount+0x3e: je 0xc015bec2 __follow_mount+0x4c + 0xc015beb6 __follow_mount+0x40: cmpl $0x0,(%eax) + 0xc015beb9 __follow_mount+0x43: jne 0xc015bebf __follow_mount+0x49 + 0xc015bebb __follow_mount+0x45: ud2a + 0xc015bebd __follow_mount+0x47: jmp 0xc015bebd __follow_mount+0x47 + 0xc015bebf __follow_mount+0x49: lock incl (%eax) + 0xc015bec2 __follow_mount+0x4c: mov %eax,0x4(%esi) + 0xc015bec5 __follow_mount+0x4f: mov $0x1,%edi + 0xc015beca __follow_mount+0x54: mov 0x4(%esi),%edx + 0xc015becd __follow_mount+0x57: cmpl $0x0,0x74(%edx) + 0xc015bed1 __follow_mount+0x5b: jne 0xc015be82 __follow_mount+0xc + 0xc015bed3 __follow_mount+0x5d: mov %edi,%eax + 0xc015bed5 __follow_mount+0x5f: pop %ebx + 0xc015bed6 __follow_mount+0x60: pop %esi + 0xc015bed7 __follow_mount+0x61: pop %edi + 0xc015bed8 __follow_mount+0x62: pop %ebp + 0xc015bed9 __follow_mount+0x63: ret + + [0]kdb> bb1 0xc015bed9 + bb_pass1: func_name __follow_mount func_start 0xc015be76 func_end 0xc015beda + bb_pass1: end + bb[0] start 0xc015be76 end 0xc015be80 drop_through 0 + bb[1] start 0xc015be82 end 0xc015beac drop_through 1 + bb[2] start 0xc015bead end 0xc015bebb drop_through 0 + +Note that the ud2a (BUG) instruction at 0xc015bebb ends bb[2]. + + bb[3] start 0xc015bebd end 0xc015bebd drop_through 0 + +bb[3] is peculiar, it is a jmp to itself, nothing else refers to 0xc015bebd and +you cannot drop through from the previous instruction because ud2a kills the +kernel. The i386 and x86_64 BUG() macros contain for(;;) after ud2a, for no +good reason that I can see (is there old hardware that does not abort on ud2a?). +ia64 and the generic versions of BUG() do not contain for(;;). for(;;) after +ud2a generates a branch to itself than can never be executed. + + bb[4] start 0xc015bebf end 0xc015bec1 drop_through 1 + bb[5] start 0xc015bec2 end 0xc015bec9 drop_through 1 + bb[6] start 0xc015beca end 0xc015bed2 drop_through 1 + bb[7] start 0xc015bed3 end 0xc015bed9 drop_through 0 + bb_jmp[0] from 0xc015be80 to 0xc015beca drop_through 0 + bb_jmp[1] from 0xc015be8d to 0xc015bed3 drop_through 0 + bb_jmp[2] from 0xc015be99 to 0xc015bead drop_through 0 + bb_jmp[3] from 0xc015be9f to 0xc015bead drop_through 0 + bb_jmp[4] from 0xc015beb4 to 0xc015bec2 drop_through 0 + bb_jmp[5] from 0xc015beb9 to 0xc015bebf drop_through 0 + bb_jmp[6] from 0xc015bebd to 0xc015bebd drop_through 0 + bb_jmp[7] from 0xc015bed1 to 0xc015be82 drop_through 0 + bb_jmp[8] from 0xc015beac to 0xc015bead drop_through 1 + bb_jmp[9] from 0xc015bec1 to 0xc015bec2 drop_through 1 + bb_jmp[10] from 0xc015bec9 to 0xc015beca drop_through 1 + bb_jmp[11] from 0xc015bed2 to 0xc015bed3 drop_through 1 + +Apart from bb[0] and the special case bb[3], all the other blocks are part of a +cycle. That cycle goes bb[0] -> bb[6]. bb[6] -> {bb[1], bb[7]}. bb[1] -> +{bb[2], bb[7]}. bb[2] -> {bb[4], bb[5]}. bb[4] -> bb[5]. bb[5] -> bb[6] and +back to the start. bb[7] ends with 'ret', it does not feed into other blocks. + + bb_pass2: start + + bb_pass2_do_changed_blocks: allow_missing 0 + + bb[0] + [ ... detail snipped ... ] + matched: from 0xc015be80 to 0xc015beca drop_through 0 bb_jmp[0] + new state c07286d8 + + bb_pass2_do_changed_blocks: allow_missing 1 + +Because of the cycle, only bb[0] can be processed with 0 missing inputs, all the +other blocks have at least one missing input. Call bb_pass2_do_changed_blocks() +again, this time allowing one missing input per blocks. + + bb[6] + first state c07286d8 + [ ... detail snipped ... ] + matched: from 0xc015bed2 to 0xc015bed3 drop_through 1 bb_jmp[11] + reuse bb_jmp[7] + + bb[7] + first state c0728730 + [ ... detail snipped ... ] + + bb[1] + first state c0728730 + [ ... detail snipped ... ] + matched: from 0xc015beac to 0xc015bead drop_through 1 bb_jmp[8] + reuse bb_jmp[1] + + bb[2] + first state c0728788 + [ ... detail snipped ... ] + merging state c0728788 + merging state c0728788 + [ ... detail snipped ... ] + matched: from 0xc015beb9 to 0xc015bebf drop_through 0 bb_jmp[5] + reuse bb_jmp[1] + + bb[4] + first state c0728788 + [ ... detail snipped ... ] + matched: from 0xc015bec1 to 0xc015bec2 drop_through 1 bb_jmp[9] + reuse bb_jmp[1] + + bb[5] + first state c0728788 + [ ... detail snipped ... ] + merging state c0728788 + [ ... detail snipped ... ] + matched: from 0xc015bec9 to 0xc015beca drop_through 1 bb_jmp[10] + reuse bb_jmp[1] + + bb[6] + first state c07286d8 + [ ... detail snipped ... ] + merging state c0728788 + matched: from 0xc015bed2 to 0xc015bed3 drop_through 1 bb_jmp[11] + reuse bb_jmp[1] + +Note the rescan of bb[6]. The first scan only had one input from bb[0]. After +traversing the cycle and getting back from bb[5] to bb[6], bb[6] now has more +inputs so we need to rescan it. With the additional input, the output state +from bb[6] has changed since the first scan, which means that every block it +feeds has to be rescanned. bb[6] feeds bb[1] and bb[7]. + + bb[7] + first state c0728788 + [ ... detail snipped ... ] + merging state c0728788 + [ ... detail snipped ... ] + +bb[7] being rescanned, this time it has data for both its inputs. + + bb[1] + first state c0728788 + [ ... detail snipped ... ] + matched: from 0xc015beac to 0xc015bead drop_through 1 bb_jmp[8] + no state change + +bb[1] is being rescanned because the input from bb[6] has changed, however the +rescan of bb[1] reports 'no state change', the changed input from bb[6] did not +affect the final output state from bb[1]. Because the output state from bb[1] +has not changed since the previous scan, there is no need to rescan bb[2], bb[7] +or bb[4]. Since bb[4] is not being rescanned, there is no need to rescan bb[5] +or bb[6] and the cycle is closed. --- /dev/null +++ b/Documentation/kdb/kdb.mm @@ -0,0 +1,492 @@ +.TH KDB 8 "September 21, 2005" +.hy 0 +.SH NAME +Built-in Kernel Debugger for Linux - v4.4 +.SH "Overview" +This document describes the built-in kernel debugger available +for linux. This debugger allows the programmer to interactively +examine kernel memory, disassemble kernel functions, set breakpoints +in the kernel code and display and modify register contents. +.P +A symbol table is included in the kernel image and in modules which +enables all non-stack symbols (including static symbols) to be used as +arguments to the kernel debugger commands. +.SH "Getting Started" +To include the kernel debugger in a linux kernel, use a +configuration mechanism (e.g. xconfig, menuconfig, et. al.) +to enable the \fBCONFIG_KDB\fP option. Additionally, for accurate +stack tracebacks, it is recommended that the \fBCONFIG_FRAME_POINTER\fP +option be enabled (if present). \fBCONFIG_FRAME_POINTER\fP changes the compiler +flags so that the frame pointer register will be used as a frame +pointer rather than a general purpose register. +.P +After linux has been configured to include the kernel debugger, +make a new kernel with the new configuration file (a make clean +is recommended before making the kernel), and install the kernel +as normal. +.P +You can compile a kernel with kdb support but have kdb off by default, +select \fBCONFIG_KDB_OFF\fR. Then the user has to explicitly activate +kdb by booting with the 'kdb=on' flag or, after /proc is mounted, by +.nf + echo "1" > /proc/sys/kernel/kdb +.fi +You can also do the reverse, compile a kernel with kdb on and +deactivate kdb with the boot flag 'kdb=off' or, after /proc is mounted, +by +.nf + echo "0" > /proc/sys/kernel/kdb +.fi +.P +When booting the new kernel, the 'kdb=early' flag +may be added after the image name on the boot line to +force the kernel to stop in the kernel debugger early in the +kernel initialization process. 'kdb=early' implies 'kdb=on'. +If the 'kdb=early' flag isn't provided, then kdb will automatically be +invoked upon system panic or when the \fBPAUSE\fP key is used from the +keyboard, assuming that kdb is on. Older versions of kdb used just a +boot flag of 'kdb' to activate kdb early, this is no longer supported. +.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), you +must also have a user space program such as agetty set up to read from +the serial console. +The control sequence \fBKDB\fP on the serial port will cause the +kernel debugger to be entered, assuming that kdb is on, that some +program is reading from the serial console, at least one cpu is +accepting interrupts and the serial console driver is still usable. +.P +\fBNote:\fR\ When the serial console sequence consists of multiple +characters such as KDB then all but the last character are passed +through to the application that is reading from the serial console. +After exiting from kdb, you should use backspace to delete the rest of +the control sequence. +.P +You can boot with kdb activated but without the ability to enter kdb +via any keyboard sequence. +In this mode, kdb will only be entered after a system failure. +Booting with kdb=on-nokey will activate kdb but ignore keyboard +sequences that would normally drop you into kdb. +kdb=on-nokey is mainly useful when you are using a PC keyboard and your +application needs to use the Pause key. +You can also activate this mode by +.nf + echo "2" > /proc/sys/kernel/kdb +.fi +.P +If the console is sitting on the login prompt when you enter kdb, then +the login command may switch into upper case mode. +This is not a kdb bug, it is a "feature" of login - if the userid is +all upper case then login assumes that you using a TeleType (circa +1960) which does not have lower case characters. +Wait 60 seconds for login to timeout and it will switch back to lower +case mode. +.P +\fBNote:\fR\ Your distributor may have chosen a different kdb +activation sequence for the serial console. +Consult your distribution documentation. +.P +If you have both a keyboard+video and a serial console, you can use +either for kdb. +Define both video and serial consoles with boot parameters +.P +.nf + console=tty0 console=ttyS0,38400 +.fi +.P +Any kdb data entered on the keyboard or the serial console will be echoed +to both. +.P +If you are using a USB keyboard then kdb commands cannot be entered +until the kernel has initialised the USB subsystem and recognised the +keyboard. +Using kdb=early with a USB keyboard will not work, the USB subsystem is +initialised too late. +.P +While kdb is active, the keyboard (not serial console) indicators may strobe. +The caps lock and scroll lock lights will turn on and off, num lock is not used +because it can confuse laptop keyboards where the numeric keypad is mapped over +the normal keys. +On exit from kdb the keyboard indicators will probably be wrong, they will not match the kernel state. +Pressing caps lock twice should get the indicators back in sync with +the kernel. +.SH "Basic Commands" +There are several categories of commands available to the +kernel debugger user including commands providing memory +display and modification, register display and modification, +instruction disassemble, breakpoints and stack tracebacks. +Any command can be prefixed with '-' which will cause kdb to ignore any +errors on that command, this is useful when packaging commands using +defcmd. +A line whose first non-space character is '#' is printed and ignored. +.P +The following table shows the currently implemented standard commands, +these are always available. Other commands can be added by extra +debugging modules, type '?' at the kdb prompt to get a list of all +available commands. +.DS +.TS +box, center; +l | l +l | l. +Command Description +_ +bc Clear Breakpoint +bd Disable Breakpoint +be Enable Breakpoint +bl Display breakpoints +bp Set or Display breakpoint +bph Set or Display hardware breakpoint +bpa Set or Display breakpoint globally +bpha Set or Display hardware breakpoint globally +bt Stack backtrace for current process +btp Stack backtrace for specific process +bta Stack backtrace for all processes +btc Cycle over all live cpus and backtrace each one +cpu Display or switch cpus +dmesg Display system messages +defcmd Define a command as a set of other commands +ef Print exception frame +env Show environment +go Restart execution +handlers Control the display of IA64 MCA/INIT handlers +help Display help message +id Disassemble Instructions +kill Send a signal to a process +ll Follow Linked Lists +lsmod List loaded modules +md Display memory contents +mdWcN Display memory contents with width W and count N. +mdp Display memory based on a physical address +mdr Display raw memory contents +mds Display memory contents symbolically +mm Modify memory contents, words +mmW Modify memory contents, bytes +per_cpu Display per_cpu variables +pid Change the default process context +ps Display process status +reboot Reboot the machine +rd Display register contents +rm Modify register contents +rq Display runqueue for one cpu +rqa Display runqueue for all cpus +set Add/change environment variable +sr Invoke SysReq commands +ss Single step a cpu +ssb Single step a cpu until a branch instruction +stackdepth Print the stack depth for selected processes +summary Summarize the system +.TE +.DE +.P +Some commands can be abbreviated, such commands are indicated by a +non-zero \fIminlen\fP parameter to \fBkdb_register\fP; the value of +\fIminlen\fP being the minimum length to which the command can be +abbreviated (for example, the \fBgo\fP command can be abbreviated +legally to \fBg\fP). +.P +If an input string does not match a command in the command table, +it is treated as an address expression and the corresponding address +value and nearest symbol are shown. +.P +Some of the commands are described here. +Information on the more complicated commands can be found in the +appropriate manual pages. +.TP 8 +cpu +With no parameters, it lists the available cpus. +\&'*' after a cpu number indicates a cpu that did not respond to the kdb +stop signal. +\&'+' after a cpu number indicates a cpu for which kdb has some data, but +that cpu is no longer responding to kdb, so you cannot switch to it. +This could be a cpu that has failed after entering kdb, or the cpu may +have saved its state for debugging then entered the prom, this is +normal for an IA64 MCA event. +\&'I' after a cpu number means that the cpu was idle before it entered +kdb, it is unlikely to contain any useful data. +\&'F' after a cpu number means that the cpu is offline. +There is currenly no way to distinguish between cpus that used to be +online but are now offline and cpus that were never online, the kernel +does not maintain the information required to separate those two cases. +.I cpu +followed by a number will switch to that cpu, you cannot switch to +a cpu marked '*', '+' or 'F'. +This command is only available if the kernel was configured for SMP. +.TP 8 +dmesg [lines] [adjust] +Displays the system messages from the kernel buffer. +If kdb logging is on, it is disabled by dmesg and is left as disabled. +With no parameters or a zero value for 'lines', dmesg dumps the entire +kernel buffer. +If lines is specified and is positive, dmesg dumps the last 'lines' +from the buffer. +If lines is specified and is negative, dmesg dumps the first 'lines' +from the buffer. +If adjust is specified, adjust the starting point for the lines that +are printed. +When 'lines' is positive, move the starting point back by 'adjust' +lines, when 'lines' is negative, move the starting point forward by +\&'adjust' lines. +.I dmesg -100 +will dump 100 lines, from the start of the buffer. +.I dmesg 100 +will dump 100 lines, starting 100 lines from the end of the buffer, +.I dmesg 100 100 +will dump 100 lines, starting 200 lines from the end of the buffer. +.I dmesg -100 100 +will dump 100 lines, starting 100 lines from the start of the buffer. +.TP 8 +defcmd +Defines a new command as a set of other commands, all input until +.I endefcmd +is saved and executed as a package. +.I defcmd +takes three parameters, the command name to be defined and used to +invoke the package, a quoted string containing the usage text and a +quoted string containing the help text for the command. +When using defcmd, it is a good idea to prefix commands that might fail +with '-', this ignores errors so the following commands are still +executed. +For example, +.P +.nf + defcmd diag "" "Standard diagnostics" + set LINES 2000 + set BTAPROMPT 0 + -id %eip-0x40 + -cpu + -ps + -dmesg 80 + -bt + -bta + endefcmd +.fi +.P +When used with no parameters, defcmd prints all the defined commands. +.TP 8 +go +Continue normal execution. +Active breakpoints are reestablished and the processor(s) allowed to +run normally. +To continue at a specific address, use +.I rm +to change the instruction pointer then go. +.TP 8 +handlers +Control the display of IA64 MCA/INIT handlers. +The IA64 MCA/INIT handlers run on separate tasks. +During an MCA/INIT event, the active tasks are typically the handlers, +rather than the original tasks, which is not very useful for debugging. +By default, KDB hides the MCA/INIT handlers so commands such as ps and +btc will display the original task. +You can change this behaviour by using +.I handlers show +to display the MCA/INIT handlers instead of the original tasks or use +.I handlers hide +(the default) to hide the MCA/INIT handlers and display the original +tasks. +.I handlers status +will list the address of the handler task and the original task for +each cpu. +\fBNote:\fR\ If the original task was running in user space or it +failed any of the MCA/INIT verification tests then there is no original +task to display. +In this case, the handler will be displayed even if +.I handlers hide +is set and +.I handlers status +will not show an original task. +.TP 8 +id +Disassemble instructions starting at an address. +Environment variable IDCOUNT controls how many lines of disassembly +output the command produces. +.TP 8 +kill +Internal command to send a signal (like kill(1)) to a process. +kill -signal pid. +.TP 8 +lsmod +Internal command to list modules. +This does not use any kernel nor user space services so can be used at any time. +.TP 8 +per_cpu [] [] +Display the values of a per_cpu variable, the variable_name is +specified without the \fIper_cpu__\fR prefix. +Length is the length of the variable, 1-8, if omitted or 0 it defaults +to the size of the machine's register. +To display the variable on a specific cpu, the third parameter is the +cpu number. +When the third parameter is omitted, the variable's value is printed +from all cpus, except that zero values are suppressed. +For each cpu, per_cpu prints the cpu number, the address of the +variable and its value. +.TP 8 +pid +Change the current process context, with no parameters it displays the +current process. +The current process is used to display registers, both kernel and user +space. +It is also used when dumping user pages. +.I pid R +resets to the original process that was running when kdb was entered. +This command is useful if you have been looking at other processes and/or +cpus and you want to get back to the original process. +It does not switch cpus, it only resets the context to the original process. +.TP 8 +reboot +Reboot the system, with no attempt to do a clean close down. +.TP 8 +rq +Display the runqueues for the specified cpu. +.TP 8 +rqa +Display the runqueues for all cpus. +.TP 8 +stackdepth +Print the stack usage for processes using more than the specified +percentage of their stack. +If percentage is not supplied, it defaults to 60. +This command is only implemented on i386 and ia64 architectures, +patches for other architectures will be gratefully accepted. +.TP 8 +summary +Print a summary of the system, including the time (no timezone is +applied), uname information and various critical system counters. +.SH INITIAL KDB COMMANDS +kdb/kdb_cmds is a plain text file where you can define kdb commands +which are to be issued during kdb_init(). One command per line, blank +lines are ignored, lines starting with '#' are ignored. kdb_cmds is +intended for per user customization of kdb, you can use it to set +environment variables to suit your hardware or to set standard +breakpoints for the problem you are debugging. This file is converted +to a small C object, compiled and linked into the kernel. You must +rebuild and reinstall the kernel after changing kdb_cmds. This file +will never be shipped with any useful data so you can always override +it with your local copy. Sample kdb_cmds: +.P +.nf +# Initial commands for kdb, alter to suit your needs. +# These commands are executed in kdb_init() context, no SMP, no +# processes. Commands that require process data (including stack or +# registers) are not reliable this early. set and bp commands should +# be safe. Global breakpoint commands affect each cpu as it is booted. + +set LINES=50 +set MDCOUNT=25 +set RECURSE=1 +bp sys_init_module +.fi +.SH INTERRUPTS AND KDB +When a kdb event occurs, one cpu (the initial cpu) enters kdb state. +It uses a cross system interrupt to interrupt the +other cpus and bring them all into kdb state. All cpus run with +interrupts disabled while they are inside kdb, this prevents most +external events from disturbing the kernel while kdb is running. +.B Note: +Disabled interrupts means that any I/O that relies on interrupts cannot +proceed while kdb is in control, devices can time out. The clock tick +is also disabled, machines will lose track of time while they are +inside kdb. +.P +Even with interrupts disabled, some non-maskable interrupt events will +still occur, these can disturb the kernel while you are debugging it. +The initial cpu will still accept NMI events, assuming that kdb was not +entered for an NMI event. Any cpu where you use the SS or SSB commands +will accept NMI events, even after the instruction has finished and the +cpu is back in kdb. This is an unavoidable side effect of the fact that +doing SS[B] requires the cpu to drop all the way out of kdb, including +exiting from the event that brought the cpu into kdb. Under normal +circumstances the only NMI event is for the NMI oopser and that is kdb +aware so it does not disturb the kernel while kdb is running. +.P +Sometimes doing SS or SSB on ix86 will allow one interrupt to proceed, +even though the cpu is disabled for interrupts. I have not been able +to track this one down but I suspect that the interrupt was pending +when kdb was entered and it runs when kdb exits through IRET even +though the popped flags are marked as cli(). If any ix86 hardware +expert can shed some light on this problem, please notify the kdb +maintainer. +.SH RECOVERING FROM KDB ERRORS +If a kdb command breaks and kdb has enough of a recovery environment +then kdb will abort the command and drop back into mainline kdb code. +This means that user written kdb commands can follow bad pointers +without killing kdb. Ideally all code should verify that data areas +are valid (using kdb_getarea) before accessing it but lots of calls to +kdb_getarea can be clumsy. +.P +The sparc64 port does not currently provide this error recovery. +If someone would volunteer to write the necessary longjmp/setjmp +code, their efforts would be greatly appreciated. In the +meantime, it is possible for kdb to trigger a panic by accessing +a bad address. +.SH DEBUGGING THE DEBUGGER +kdb has limited support for debugging problems within kdb. If you +suspect that kdb is failing, you can set environment variable KDBDEBUG +to a bit pattern which will activate kdb_printf statements within kdb. +See include/linux/kdb.h, KDB_DEBUG_FLAG_xxx defines. For example +.nf + set KDBDEBUG=0x60 +.fi +activates the event callbacks into kdb plus state tracing in sections +of kdb. +.nf + set KDBDEBUG=0x18 +.fi +gives lots of tracing as kdb tries to decode the process stack. +.P +You can also perform one level of recursion in kdb. If environment +variable RECURSE is not set or is 0 then kdb will either recover from +an error (if the recovery environment is satisfactory) or kdb will +allow the error to percolate, usually resulting in a dead system. When +RECURSE is 1 then kdb will recover from an error or, if there is no +satisfactory recovery environment, it will drop into kdb state to let +you diagnose the problem. When RECURSE is 2 then all errors drop into +kdb state, kdb does not attempt recovery first. Errors while in +recursive state all drop through, kdb does not even attempt to recover +from recursive errors. +.SH KEYBOARD EDITING +kdb supports a command history, which can be accessed via keyboard +sequences. +It supports the special keys on PC keyboards, control characters and +vt100 sequences on a serial console or a PC keyboard. +.P +.DS +.TS +box, center; +l | l | l l | l +l | l | l l | l. +PC Special keys Control VT100 key Codes Action +_ +Backspace ctrl-H Backspace 0x7f Delete character to the left of the cursor +Delete ctrl-D Delete \\e[3~ Delete character to the right of the cursor +Home ctrl-A Home \\e[1~ Go to start of line +End ctrl-E End \\e[4~ Go to end of line +Up arrow ctrl-P Up arrow \\e[A Up one command in history +Down arrow ctrl-N Down arrow \\e[B Down one command in history +Left arrow ctrl-B Left arrow \\e[D Left one character in current command +Right arrow ctrl-F Right arrow \\e[C Right one character in current command +.TE +.DE +.P +There is no toggle for insert/replace mode, kdb editing is always in +insert mode. +Use delete and backspace to delete characters. +.P +kdb also supports tab completion for kernel symbols +Type the start of a kernel symbol and press tab (ctrl-I) to complete +the name +If there is more than one possible match, kdb will append any common +characters and wait for more input, pressing tab a second time will +display the possible matches +The number of matches is limited by environment variable DTABCOUNT, +with a default of 30 if that variable is not set. +.SH AUTHORS +Scott Lurndal, Richard Bass, Scott Foehner, Srinivasa Thirumalachar, +Masahiro Adegawa, Marc Esipovich, Ted Kline, Steve Lord, Andi Kleen, +Sonic Zhang. +.br +Keith Owens - kdb maintainer. +.SH SEE ALSO +.P +linux/Documentation/kdb/kdb_{bp,bt,env,ll,md,ps,rd,sr,ss}.man --- /dev/null +++ b/Documentation/kdb/kdb_bp.man @@ -0,0 +1,197 @@ +.TH BD 1 "July 12, 2004" +.SH NAME +bp, bpa, bph, bpha, bd, bc, be, bl \- breakpoint commands +.SH SYNOPSIS +bp \fIaddress-expression\fP +.LP +bpa \fIaddress-expression\fP +.LP +bph \fIaddress-expression\fP [\f(CWDATAR|DATAW|DATAA|IO\fP [\fIlength\fP]] +.LP +bpha \fIaddress-expression\fP [\f(CWDATAR|DATAW|DATAA|IO\fP [\fIlength\fP]] +.LP +bd \fIbreakpoint-number\fP +.LP +bc \fIbreakpoint-number\fP +.LP +be \fIbreakpoint-number\fP +.LP +bl +.SH DESCRIPTION +.hy 0 +The +.B bp +family of commands are used to establish a breakpoint. +The \fIaddress-expression\fP may be a numeric value (decimal or +hexidecimal), a symbol name, a register name preceeded by a +percent symbol '%', or a simple expression consisting of a +symbol name, an addition or subtraction character and a numeric +value (decimal or hexidecimal). +.P +\fBbph\fP and \fBbpha\fP will force the use of a hardware register, provided +the processor architecture supports them. +.P +The \fIaddress-expression\fP may also consist of a single +asterisk '*' symbol which indicates that the command should +operate on all existing breakpoints (valid only for \fBbc\fP, +\fBbd\fP and \fBbe\fP). +.P +Four different types of +breakpoints may be set: + +.TP 8 +Instruction +Causes the kernel debugger to be invoked from the debug exception +path when an instruction is fetched from the specified address. This +is the default if no other type of breakpoint is requested or when +the \fBbp\fP command is used. + +.TP 8 +DATAR +Causes the kernel debugger to be entered when data of length +\fIlength\fP is read from or written to the specified address. +This type of breakpoint must use a processor debug register which +places an architecture dependent limit on the number of data and I/O +breakpoints that may be established. On arm mode XScale platform +(thumb mode is not supported yet), +debugger is triggered by reading from the specified address. +The \fBbph\fP or \fBbpha\fP commands must be used. + +.TP 8 +DATAW +Enters the kernel debugger when data of length \fIlength\fP +is written to the specified address. \fIlength\fP defaults +to four bytes if it is not explicitly specified. +Note that the processor may have already overwritten the prior data at +the breakpoint location before the kernel debugger is invoked. +The prior data should be saved before establishing the breakpoint, if +required. On arm mode XScale platform, the debugger is triggered +after having overwritten the specified address. +The \fBbph\fP or \fBbpha\fP commands must be used. + +.TP 8 +IO +Enters the kernel debugger when an \fBin\fP or \fBout\fP instruction +targets the specified I/O address. The \fBbph\fP or \fBbpha\fP +commands must be used. This type of breakpoint is not valid in +arm mode XScale platform. This option is not valid in arm +mode XScale platform. + +.TP 8 +DATAA +Enters the kernel debugger after the data in specified address has +been accessed (read or write), this option is only used in arm +mode XScale platform. + +.P +The +.B bpha +command will establish a breakpoint on all processors in an +SMP system. This command is not available in an uniprocessor +kernel. +.P +The +.B bd +command will disable a breakpoint without removing it from the kernel +debugger's breakpoint table. +This can be used to keep breakpoints in the table without exceeding the +architecture limit on breakpoint registers. +A breakpoint-number of \fI*\fR will disable all break points. +.P +The +.B be +command will re-enable a disabled breakpoint. +A breakpoint-number of \fI*\fR will enable all break points. +.P +The +.B bc +command will clear a breakpoint from the breakpoint table. +A breakpoint-number of \fI*\fR will clear all break points. +.P +The +.B bl +command will list the existing set of breakpoints. +.SH LIMITATIONS +There is a compile time limit of sixteen entries in the +breakpoint table at any one time. +.P +There are architecture dependent limits on the number of hardware +breakpoints that can be set. +.IP ix86 8 +Four. +.PD 0 +.IP xscale 8 +Two for insruction breakpoints and another two for data breakpoint. +.PD 0 +.IP ia64 8 +? +.PD 0 +.IP sparc64 8 +None. +.PD 1 +When issuing the "go" command after entering the debugger due to +a breakpoint, kdb will silently perform a single step in order to +reapply the breakpoint. The sparc64 port has some limitations on +single stepping, which may limit where a breakpoint may be safely +set. Please read the man page for \fBss\fP for more information. +.SH ENVIRONMENT +The breakpoint subsystem does not currently use any environment +variables. +.SH SMP CONSIDERATIONS +Using +.B bc +is risky on SMP systems. +If you clear a breakpoint when another cpu has hit that breakpoint but +has not been processed then it may not be recognised as a kdb +breakpoint, usually resulting in incorrect program counters and kernel +panics. +It is safer to disable the breakpoint with +.BR bd , +then +.B go +to let any other processors that are waiting on the breakpoint to +clear. +After all processors are clear of the disabled breakpoint then it is +safe to clear it using +.BR bc . +.P +Breakpoints which use the processor breakpoint registers +are only established on the processor which is +currently active. If you wish breakpoints to be universal +use the +.B bpa +or +.B bpha +commands. +.SH EXAMPLES +.TP 8 +bp schedule +Sets an instruction breakpoint at the begining of the +function \fBschedule\fP. + +.TP 8 +bp schedule+0x12e +Sets an instruction breakpoint at the instruction located +at \fBschedule\fP+\fI0x12e\fP. + +.TP 8 +bph ttybuffer+0x24 dataw +Sets a data write breakpoint at the location referenced by +\fBttybuffer\fP+\fI0x24\fP for a length of four bytes. + +.TP 8 +bph 0xc0254010 datar 1 +Establishes a data reference breakpoint at address \fB0xc0254010\fP +for a length of one byte. + +.TP 8 +bp +List current breakpoint table. + +.TP 8 +bd 0 +Disable breakpoint #0. + +.TP 8 +bc * +Clear all breakpoints --- /dev/null +++ b/Documentation/kdb/kdb_bt.man @@ -0,0 +1,315 @@ +.TH BT 1 "July 20, 2007" +.SH NAME +bt \- Stack Traceback command +.SH SYNOPSIS +bt [ ] +.LP +btp +.LP +btt +.LP +bta [ DRSTZUIMA ] +.LP +btc [] +.SH DESCRIPTION +.hy 0 +The +.B bt +command is used to print a stack traceback. It uses the +current registers (see \fBrd\fP command) to determine +the starting context and attempts to provide a complete +stack traceback for the active thread. If \fIstack-frame-address\fP +is supplied, it is assumed to point to the start of a valid +stack frame and the stack will be traced back from that +point. +On x86 architecture, \fIstack-frame-address\fP must be the stack address of a +saved \fB%eip\fP (\fB%rip\fP for x86_64) value from a \fBcall\fP instruction. +.P +The \fBbtp\fP command will analyze the stack for the given +process identification (see the \fBps\fP command). +\fBbtp\fP sets the current process for any following register display or update +commands. +.P +The \fBbtt\fP command will analyze the stack for the given task +structure. +It is exactly equivalent to \fBbtp\fR on the pid extracted from the +task structure. +\fBbtt\fP sets the current process for any following register display or update +commands. +.P +The \fBbta\fP command lists the stack for all processes in the desired +state. +Without any parameters, \fBbta\fP gives a backtrace for all useful processes. +If a parameter is specified, it is a single string consisting of the +letters D, R, S, T, Z, U, I, M and A in any order. +See the kdb \fBps\fR man page for more details. +\fBbta\fP does not change the current process. +.P +The \fBbtc\fP command will analyze the stack for the current process on +a specified cpu or, if no cpu number is supplied, for the current +process on all cpus. +It does not switch to the other cpus, instead it uses the task +structures to identify and issue \fBbtt\fR against the current task on +the desired cpus. +\fBbtc\fP with no arguments does not change the current process. +\fBbtc\fP with a cpu number sets the current process for any following register +display or update commands. +.P +For each function, the stack trace prints at least two lines. +The first line contains four or five fields\ :- +.IP * 3 +The pointer to the stack frame. +.PD 0 +.IP * 3 +The current address within this frame. +.IP * 3 +The address converted to a function name (actually the first non-local +label which is <= the address). +.IP * 3 +The offset of the address within the function. +.IP * 3 +Any parameters to the function. +.PD 1 +.PP +If environment variable NOSECT is set to 0 then the next line contains +five fields which are designed to make it easier to match the trace +against the kernel code\ :- +.IP * 3 +The module name that contains the address, "kernel" if it is in the +base kernel. +.PD 0 +.IP * 3 +The section name that contains the address (not available on 2.6 kernels). +.IP * 3 +The start address of the section (not available on 2.6 kernels). +.IP * 3 +The start address of the function. +.IP * 3 +The end address of the function (the first non-local label which is > +the address). +.PD 1 +.PP +If arguments are being converted to symbols, any argument which +converts to a kernel or module address is printed as\ :- +.IP * 3 +Argument address. +.PD 0 +.IP * 3 +The module name that contains the address, "kernel" if it is in the +base kernel. +.IP * 3 +The symbol name the argument maps to. +.IP * 3 +The offset of the argument from the symbol, suppressed if 0. +.PD 1 +.P +On architectures that use nested stacks, the backtrace will indicate a +switch to a new stack by printing a line of equal signs and the type of +stack. +.SH MATCHING TRACE TO KERNEL CODE +The command "objdump\ -S" will disassemble an object and, if the code +was compiled with debugging (gcc flag -g), objdump will interleave the +C source lines with the generated object. +.PP +A complete objdump of the kernel or a module is too big, normally you +only want specific functions. +By default objdump will only print the .text section but Linux uses +other section names for executable code. +When objdump prints relocatable objects (modules) it uses an offset of +0 which is awkward to relate to the stack trace. +The five fields which are printed for each function are designed to +make it easier to match the stack trace against the kernel code using +"objdump\ -S". +.PP +If the function is in the kernel then you need the section name, the +start and end address of the function. The command is +.PP +.nf + objdump -S -j \\ + --start-address= \\ + --stop-address= \\ + /usr/src/linux/vmlinux +.fi +.PP +If the function is in a module then you need the section name, the +start address of the section, the start and end address of the +function, the module name. The command is +.PP +.nf + objdump -S -j \\ + --adjust-vma= \\ + --start-address= \\ + --stop-address= \\ + /path/to/module/.o +.fi +.PP +Unfortunately the 2.6 kernel does not provide the information required +to locate the start of the section, which makes it very difficult to +perform a reliable objdump on a module. +.PP +All addresses to objdump must be preceded by '0x' if they are in hex, +objdump does not assume hex. +The stack trace values are printed with leading '0x' to make it easy to +run objdump. +.SH LIMITATIONS +Some architectures pass parameters in registers; ia64, x86_64 and i386 (with +gcc flag -mregparm) fall into this category. +On these architectures, the compiler may reuse input parameter registers as +scratch space. +For example, if a function takes a pointer to a structure and only accesses one +field in that structure, the compiler may calculate the address of the field by +adding a value to the input register. +Once the input register has been updated, it no longer points to the +start of the structure, but to some field within it. +This also occurs with array pointers, the compiler may update the input pointer +directly, leaving it pointing to some element of the array instead of the start +of the array. +Always treat parameter values that have been passed in registers with extreme +suspicion, the compiler may have changed the value. +The x86 backtrace can generally identify register parameters that are no longer +valid, it prints them as 'invalid' instead of as a misleading number. +The ia64 backtrace cannot identify parameter registers that have been +overwritten. +.P +x86 architectures do not have full unwind information in the kernel. +The KDB backtrace on x86 performs code decomposition and analysis to track the +frames on the call stack (including stack switches) and to locate parameters. +if this code analysis does not yield a valid result, KDB falls back on the old +method of scanning the process stack and printing anything that looks like a +kernel address. +This old method is unreliable (it produces lots of false positives in the +trace) and cannot track parameters at all, so no parameters are printed. +If you get an x86 backtrace that falls back to the old method, read +Documentation/kdb/bt_x86 and follow the steps listed to get diagnostics and to +submit a bug report. +.P +There are a lot of functions in the kernel which take some arguments then do +nothing except call another function with the same initial arguments, sometimes +adding parameters at the end. For example\ :- +.nf +.na +.ft CW + +int ipv4_doint_and_flush_strategy(ctl_table *table, int __user *name, int nlen, + void __user *oldval, size_t __user *oldlenp, + void __user *newval, size_t newlen) +{ + int ret = devinet_conf_sysctl(table, name, nlen, oldval, oldlenp, + newval, newlen); + + if (ret == 1) + rt_cache_flush(0); + + return ret; +} +.ad b +.fi +.P +ipv4_doint_and_flush_strategy() passes all its parameters directly to +devinet_conf_sysctl() and makes no other use of those parameters, +so ipv4_doint_and_flush_strategy is a 'pass through' function. +The x86_64 calling sequence mandates that the first 6 parameters are passed in +registers, with other parameters being passed on stack. +The i386 calling sequence with -mregparm=3 (which is the default since about +2.6.18) passes the first 3 parameters in registers, with other parameters being +passed on stack. +The only exceptions to the above calling sequence are for functions declared as +asmlinkage or functions with a variable number of parameters (e.g. printk). +.P +When a pass through function calls another function, the first 3 (i386) or 6 +(x86) parameters are already in their correct registers so the pass through +function does not need to access the registers, which means that there are no +references to these registers in the assembler code for the function. +Users still want to see those arguments so the x86 backtrace has to assume that +if\ :- +.IP * 2 +There are parameters passed on the stack and +.IP * +There are no code references to parameters passed in registers and +.IP * +The function is not a known asmlinkage or variadic function, then +there are pass through register arguments. +.P +The x86 backtrace will warn you when it makes this assumption, like this\ :- +.nf +.na +.ft CW + + has memory parameters but no register parameters. + Assuming it is a 'pass through' function that does not refer to its register + parameters and setting register parameters +.ad b +.fi +.P +The above 3 line message is only printed once, any future assumptions will +print a shorter message. +.P +The \fBbt\fP command may print more or less arguments for a function +than that function accepts. +For x86, trailing arguments that are passed in but not used by the function +will not be printed, resulting in fewer arguments than expected. +For ia64, the hardware does not distinguish between input and local registers, +some local registers may be printed as function arguments, resulting in more +arguments than expected. +.P +On i386, 64 bit arguments (long long) occupy two adjacent 32 bit fields. +There is no way for KDB to tell that this has occurred, so 64 bit arguments +will be printed as two separate 32 bit arguments. +.SH ENVIRONMENT +The \fBBTARGS\fP environment variable governs the maximum number +of arguments that are printed for any single function. +On IA64 hardware, there is no difference between input and local registers, the +first \fBBTARGS\fP registers are printed, up to the total limit of input plus +local registers. +Use a large value for \fBBTARGS\fP if you want to see the local registers on +IA64. +.PP +If the \fBBTSP\fP environment variable is non-zero then the entire backtrace is +printed, otherwise only the backtrace to the point of the last interrupt is +printed. +Printing the entire backtrace with 'set\ BTSP\ 1' is useful for diagnosing +problems with the backtrace algorithms. +In addition, when BTSP is non-zero, each backtrace frame may print extra lines +giving information about the stack pointers, this is architecture specific. +.PP +If the \fBBTSYMARG\fP environment variable is non-zero then any +arguments that fall within the kernel or modules are converted to symbols. +.PP +If the \fBNOSECT\fP environment variable is non-zero then the +section information is suppressed. +The default is NOSECT=1 so section data is suppressed; use set\ NOSECT=0 +to see section information. +.PP +The \fBBTAPROMPT\fP environment variable controls the prompt after each +process is listed by the \fBbta\fP command. If \fBBTAPROMPT\fP is not +set or is non-zero then \fBbta\fP issues a prompt after each process is +listed. If \fBBTAPROMPT\fP is set to zero then no prompt is issued and +all processes are listed without human intervention. +.PP +\fBbt\fR with no parameters uses the \fBPS\fR environment variable, see +the kdb \fBps\fR man page. +.SH SMP CONSIDERATIONS +None. +.SH EXAMPLES +.nf +.na +.ft CW +[0]kdb> bt +Stack traceback for pid 2873 +0xc2efc0f0 2873 2836 1 0 R 0xc2efc2a0 *mount +esp eip Function (args) +0xf65a3c88 0xc0201f9f xfs_mount_validate_sb (0xf68bcb08, 0xf68bcb48, 0x0) +0xf65a3c94 0xc0202f17 xfs_readsb+0x9d (0xf68bcb08, 0x0) +0xf65a3cc0 0xc020a72e xfs_mount+0x21d (invalid, 0xf68bc2f0, 0x0) +0xf65a3cf4 0xc021a84a vfs_mount+0x1a (invalid) +0xf65a3d04 0xc021a721 xfs_fs_fill_super+0x76 (0xf76b6200, invalid, invalid) +0xf65a3d78 0xc015ad81 get_sb_bdev+0xd4 (invalid, invalid, invalid, 0xf7257000, 0xc021a6ab, 0xf7594b38) + xfs_fs_get_sb has memory parameters but no register parameters. + Assuming it is a 'pass through' function that does not refer to its register + parameters and setting 3 register parameters +0xf65a3db4 0xc0219a3a xfs_fs_get_sb+0x21 (invalid, invalid, invalid, 0xf7257000, 0xf7594b38) +0xf65a3dcc 0xc015a992 vfs_kern_mount+0x41 (0xc04847e0, 0x0, 0xf68e9000, 0xf7257000) +0xf65a3df0 0xc015aa11 do_kern_mount+0x38 (0xf6818000, 0x0, 0xf68e9000, 0xf7257000) +0xf65a3e10 0xc016c8b0 do_mount+0x5df (0xf68e9000, 0xf65d6000, 0xf6818000, 0xc0ed0000, 0xf7257000) +0xf65a3f90 0xc016c996 sys_mount+0x6f (0x8069b50, 0x8069b60, 0x8069b70, 0xc0ed0000, 0x8069ba0) +0xf65a3fb4 0xc0102646 sysenter_past_esp+0x5f (invalid, invalid, invalid, 0x73, 0x246, 0xbfe52f50) --- /dev/null +++ b/Documentation/kdb/kdb_env.man @@ -0,0 +1,46 @@ +.TH ENV 1 "24 September 2000" +.SH NAME +env, set \- Environment manipulation commands +.SH SYNOPSIS +env +.LP +set \fIenvironment-variable\fP=\fIvalue\fP +.SH DESCRIPTION +The kernel debugger contains an environment which contains a series +of name-value pairs. Some environment variables are known to the +various kernel debugger commands and have specific meaning to the +command; such are enumerated on the respective reference material. +.P +Arbitrary environment variables may be created and used with +many commands (those which require an \fIaddress-expression\fP). +.P +The +.B env +command is used to display the current environment. +.P +The +.B set +command is used to alter an existing environment variable or +establish a new environment variable. +.SH LIMITATIONS +There is a compile-time limit of 33 environment variables. +.P +There is a compile-time limit of 512 bytes (\fBKDB_ENVBUFSIZE\fP) +of heap space available for new environment variables and for +environment variables changed from their compile-time values. +.SH ENVIRONMENT +These commands explicitly manipulate the environment. +.SH SMP CONSIDERATIONS +None. +.SH USER SETTINGS +You can include "set" commands in kdb/kdb_cmds (see kdb.mm) to define +your environment variables at kernel startup. +.SH EXAMPLES +.TP 8 +env +Display current environment settings. + +.TP 8 +set IDCOUNT=100 +Set the number of lines to display for the \fBid\fP command +to the value \fI100\fP. --- /dev/null +++ b/Documentation/kdb/kdb_ll.man @@ -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> --- /dev/null +++ b/Documentation/kdb/kdb_md.man @@ -0,0 +1,136 @@ +.TH MD 1 "August 4, 2004" +.SH NAME +md, mdWcN, mdr, mds, mm, mmW\- Memory manipulation commands +.SH SYNOPSIS +md [ \fIaddress-expression\fP [ \fIline-count\fP [\fIoutput-radix\fP ] ] ] +.LP +md\fIW\fRc\fIn\fR [ \fIaddress-expression\fP [ \fIline-count\fP [\fIoutput-radix\fP ] ] ] +.LP +mdp \fIphysical-address-expression\fP,\fIbytes\fP +.LP +mdr \fIaddress-expression\fP,\fIbytes\fP +.LP +mds [ \fIaddress-expression\fP [ \fIline-count\fP [\fIoutput-radix\fP ] ] ] +.LP +mm \fIaddress-expression\fP \fInew-contents\fP +.LP +mm\fIW\fR \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 an address is specified and 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]. If no address is specified then md resumes +after the last address printed, using the previous values of count and +radix. The start address is rounded down to a multiple of the +BYTESPERWORD (md) or width (md\fIW\fR). +.P +md uses the current value of environment variable \fBBYTESPERWORD\fP to +read the data. When reading hardware registers that require special +widths, it is more convenient to use md\fIW\fRc\fIn\fR where \fIW\fR is +the width for this command and \fRc\fIn\fR is the number of entries to +read. For example, md1c20 reads 20 bytes, 1 at a time. To continue +printing just type md, the width and count apply to following md +commands with no parameters. \fBNote:\fR The count is the number of +repeats of the width, unlike MDCOUNT which gives the number of md lines +to print. +.P +The +.B mdp +command displays the contents of physical memory, starting at the +specified physical address for the specified number of bytes. +The address is preceded by 'phys'. +.P +The +.B mdr +command displays the raw contents of memory, starting at the specified +address for the specified number of bytes. +The data is printed in one line without a leading address and no +trailing character conversion. +.B mdr +is intended for interfacing with external debuggers, it is of little +use to humans. +.P +The +.B mds +command displays the contents of memory one word per line and +attempts to correlate the contents of each word with a symbol +in the symbol table. If no symbol is found, the ascii representation +of the word is printed, otherwise the symbol name and offset from +symbol value are printed. +By default the section data is printed for kernel symbols. +.P +The +.B mm +and +\fBmm\fIW\fR +commands allow modification of memory. The bytes at the address +represented by \fIaddress-expression\fP are changed to +\fInew-contents\fP. \fInew-contents\fP is allowed to be an +\fIaddress-expression\fP. +.B mm +changes a machine word, \fBmm\fIW\fR changes \fIW\fR bytes at that +address. +.SH LIMITATIONS +None. +.SH ENVIRONMENT +.TP 8 +MDCOUNT +This environment variable (default=8) defines the number of lines +that will be displayed by each invocation of the \fBmd\fP command. + +.TP 8 +RADIX +This environment variable (default=16) defines the radix used to +print the memory contents. + +.TP 8 +BYTESPERWORD +This environment variable (default=4) selects the width of output +data when printing memory contents. Select the value two to get +16-bit word output, select the value one to get byte output. + +.TP 8 +LINES +This environment variable governs the number of lines of output +that will be presented before the kernel debugger built-in pager +pauses the output. This variable only affects the functioning +of the \fBmd\fP and \fBmds\fP if the \fBMDCOUNT\fP variable +is set to a value greater than the \fBLINES\fP variable. + +.TP 8 +NOSECT +If the \fBNOSECT\fP environment variable is non-zero then the +section information is suppressed. +The default is NOSECT=1 so section data is suppressed; use set\ NOSECT=0 +to see section information. +.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. --- /dev/null +++ b/Documentation/kdb/kdb_ps.man @@ -0,0 +1,96 @@ +.TH PS 1 "September 14, 2004" +.SH NAME +ps \- Display processes +.SH SYNOPSIS +ps [ DRSTCZEUIMA ] +.SH DESCRIPTION +The +.B ps +command displays the status of all processes in the desired state. +This command does not take any locks (all cpus should be frozen while +kdb is running) so it can safely be used to debug lock problems with +the process table. +.P +Without any parameters, \fBps\fP displays all the interesting +processes, excluding idle tasks and sleeping system daemons. +If a parameter is specified, it is a single string consisting of the +letters D, R, S, T, C, Z, E, U, I and M, in any order. +Each letter selects processes in a specific state, when multiple +letters are specified, a process will be displayed if it is in any of +the specified states. +The states are\ :- +.P +.DS +.TS +box, center; +l | l +l | l. +D Uninterruptible sleep +R Running +S Interruptible sleep +T Stopped +C Traced +Z Zombie +E Dead +U Unrunnable +I Idle task +M Sleeping system daemon +A All +.TE +.DE +.P +For state R (running), the process may not be on a cpu at the moment, +but it is ready to run. +The header line above the backtrace contains '1' in the fourth field if +the process is actually on a cpu. +.P +The idle task is run on each cpu when there is no work for that cpu to do. +Unless the idle task is servicing an interrupt, there is no point in +printing the idle task. +An idle task that is not servicing a interrupt is marked as state I, +while servicing an interrupt it is in state R. +By default, idle tasks are not printed, use \fBps\ I\fR to print them. +If the idle tasks are not being printed, the start of the \fBps\R +output contains a list of which cpus are idle. +.P +Each cpu has one or more system daemons to handle per cpu work such as +soft irqs. +A system daemon (idenified by a NULL mm pointer) that is sleeping is +marked as state M. +These processes rarely have any useful data and generate a lot of +output on large machines, so sleeping system daemons are not printed by +default. +Use \fBps\ M\fR to print them. +.P +At the start of the \fBps\fR output is a line giving the cpu status, +see the kdb \fBcpu\fR command. +.SH LIMITATIONS +None. +.SH ENVIRONMENT +.TP 8 +PS +This environment variable (default=DRSTCZEU) is used when \fBps\fR +is issued with no parameters. + +.SH SMP CONSIDERATIONS +None. +.SH EXAMPLES +.TP 8 +\fBps\fR +displays the useful tasks, suppressing idle tasks and sleeping +system daemons. + +.TP 8 +\fBps\ RD\fR +displays only tasks that are running or are in an uninterruptible +sleep. + +.TP 8 +\fBps\ DRSTCZEUIM\fR +displays all tasks. + +.TP 8 +\fBps\ A\fR +displays all tasks. +This is easier than remembering DRSTCZEUIM. + --- /dev/null +++ b/Documentation/kdb/kdb_rd.man @@ -0,0 +1,170 @@ +.TH RD 1 "September 20, 2005" +.SH NAME +rd, rm\- Register manipulation commands +.SH SYNOPSIS +rd [[c [n]]|d|u] +.LP +rm \fIregister-name\fP \fInew-contents\fP +.LP +ef
+.SH DESCRIPTION +The +.B rd +command is used to display the contents of processor and coprocessor 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. +If the bt* or pid commands have been used to change the current process then +.B rd +and +.B rm +may not be able to display any registers. +'n' argument is only used for XScale platform to identify the desired +coprocessor number, while 'd' option is not valid for XScale platform. +.P +On IA32 and IA64, 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 +On XScale, 'c' argument is used to display the +all coprocessor control registers or specified coprocessor registers by +argumnet 'n'. Argument 'u' is used to display the +registers for the current task as of the last time the current task +entered the kernel. Argument 'd' is not supported. +.P +On ix86, the +.B rm +command allows modification of a register. The following +register names are valid: \fB%eax\fP, \fB%ebx\fP, \fB%ecx\fP, +\fB%edx\fP, \fB%esi\fP, \fB%edi\fP, \fB%esp\fP, \fB%eip\fP, +and \fB%ebp\fP. Note that if two '%' symbols are used +consecutively, the register set displayed by the 'u' argument +to the \fBrd\fP command is modified. +.P +The debug registers, \fBdr0\fP through \fBdr3\fP and both +\fBdr6\fP and \fBdr7\fP can also be modified with the \fBrm\fP +command. +.P +On sparc64, the valid registers are named \fB%g0\fP through +\fB%g7\fP, \fB%l0\fP through \fB%l7\fP, \fB%o0\fP through +\fB%o7\fP, and \fB%i0\fP through \fB%i7\fP, with the exceptions +that \fB%o6\fP is called \fB%sp\fP and that \fB%i6\fP is called +\fB%fp\fP. The registers \fB%tstate\fP, \fB%tpc\fP, \fB%tnpc\fP, +\fB%y\fP, and \fB%fprs\fP provide state information at the time +the system entered kdb. Additionally, when viewing registers, two +convenience names are provided: \fB%®s\fP shows the +address on the stack of the current registers, and \fB%csp\fP +shows the current stack pointer within kdb itself. +.P +While on XScale, both the cpu registers and most coprocessor +registers can be be modified. \fIregister-name\fP can be followings like +r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, +r15, cpsr to address cpu registers. For the coprocessor registers in XSacle, +either alias name or \fICpcc[CRndd[CRmbb[Opaa]]]\fP can be used to address +the register in coprocessor cc with CRn=dd, CRm=bb and opcode2=aa. All aa, bb, cc, dd can be +1 or 2 decimal digitals, the default value is 0 when any of them is omitted. Name +acc0_h and acc0_l are used to identify the high byte and +low word of accumulator in coprocessor 0. +.P +On IA64, the parameter to +.B rd +can be d (debug registers), u (user registers at most recent entry to kernel), +i (interrupt registers), %isr (current interrupt status), s (stacked +registers), k (kernel registers). You can also specify these individual +registers - +psr, +ifs, +ip, +unat, +pfs, +rsc, +rnat, +bsps, +pr, +ldrs, +ccv, +fpsr, +b0, +b6, +b7, +r1, +r2, +r3, +r8, +r9, +r10, +r11, +r12, +r13, +r14, +r15, +r16, +r17, +r18, +r19, +r20, +r21, +r22, +r23, +r24, +r25, +r26, +r27, +r28, +r29, +r30, +r31. +.B rm +can change any of the individual registers or the stacked registers. +.P +The +.B ef +command displays an exception frame at the specified address. +.SH LIMITATIONS +Currently the \fBrm\fP command will not allow modification of the +control registers. +.P +Currently neither the \fBrd\fP command nor the \fBrm\fP command will +display or modify the model specific registers on the Pentium +and Pentium Pro families. +.SH ENVIRONMENT +None. +.SH SMP CONSIDERATIONS +None. +.SH EXAMPLES +.TP 8 +rd +Display general register set from kdb's current task. + +.TP 8 +rd c 0 +Display coprocessor 0 registers. + +.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 %acc0_h 0 +Set the contents of high byte of accumulator to zero. + +.TP 8 +rm dr0 0xc1287220 +Set the value of the \fBdr0\fB register to \f(CW0xc1287220\fP. + +.TP 8 +rm %InVLD_BTB 0 +Write 0 to coprocessor 15 register with CRn=7, CRm=5, opcode2=6. + +.TP 8 +rm %CP15CRn7CRm5Op6 0 +Same with above. --- /dev/null +++ b/Documentation/kdb/kdb_sr.man @@ -0,0 +1,68 @@ +.TH SR 1 "7 October 2002" +.SH NAME +sr \- invoke sysrq commands from kdb +.SH SYNOPSIS +sr \fIx\fP +.SH DESCRIPTION +.hy 0 +The +.B sr +command invokes the existing sysrq handler code in the kernel. +This command takes a single character which is passed to sysrq +processing, as if you had entered the sysrq key sequence followed by +that character. +.P +.B Caveats: +.P +kdb will always call the sysrq code but sysrq may be disabled. +If you expect to use sysrq functions during debugging then +.IP "" +echo "1" > /proc/sys/kernel/sysrq +.P +before starting the debug session. +Alternatively issue +.IP "" +mm4 sysrq_enabled 1 +.P +during debugging. +.P +The sysrq code prints a heading using console loglevel 7 then reverts +to the original loglevel for the rest of the sysrq processing. +If the rest of the sysrq output is printed at a level below your +current loglevel then you will not see the output on the kdb console, +the output will only appear in the printk buffer. +It is the user's responsibility to set the loglevel correctly if they +want to see the sysrq output on the console. +Issue +.IP "" +sr 7 +.P +before any other +.B sr +commands if you want to see the output on the console. +You may even have to adjust the default message loglevel in order to +see any output from +.BR sr . +See Documentation/sysctl/kernel.txt for details on setting console +loglevels via /proc. +You can also adjust the loglevel variables via kdb +.BR mm ; +on older kernels there are variables such as default_message_level, on +newer kernels all the loglevel variables are in array console_printk, +see kernel/printk.c for your kernel. +.P +Operations that require interrupt driven I/O can be invoked from kdb +.BR sr , +but they will not do anything until you type 'go' to exit from kdb +(interrupts are disabled while in kdb). +There is no guarantee that these operations will work, if the machine +entered kdb because of an error then interrupt driven I/O may already +be dead. +Do not assume that +.B sr\ s +does anything useful. +.P +The sysrq handler uses locks and calls printk which also uses locks. +If the sysrq handler or any of the sysrq functions have to wait for a +lock then they will never return and kdb will appear to hang. +Invoking sysrq code from kdb is inherently unsafe. --- /dev/null +++ b/Documentation/kdb/kdb_ss.man @@ -0,0 +1,109 @@ +.TH SS 1 "17 January 2002" +.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 +may be printed as it is executed, depending upon architecture; +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 +On sparc64, there are some circumstances where single-stepping +can be dangerous. Do not single-step across an instruction which +changes the interrupt-enable bit in %tstate. Do not single step +through code which is invoked when entering or leaving the +kernel, particularly any kernel entry code before %tl is set to +0, or any kernel exit code after %tl is set to 1. +.SH ENVIRONMENT +None. +.SH SMP CONSIDERATIONS +Other processors are held in the kernel debugger when the instruction +is traced. Single stepping though code that requires a lock which is +in use by another processor is an exercise in futility, it will never +succeed. +.SH INTERRUPT CONSIDERATIONS +When a kdb event occurs, one cpu (the initial cpu) enters kdb state. +It uses a cross system interrupt to interrupt the +other cpus and bring them all into kdb state. All cpus run with +interrupts disabled while they are inside kdb, this prevents most +external events from disturbing the kernel while kdb is running. +.B Note: +Disabled interrupts means that any I/O that relies on interrupts cannot +proceed while kdb is in control, devices can time out. The clock tick +is also disabled, machines will lose track of time while they are +inside kdb. +.P +Even with interrupts disabled, some non-maskable interrupt events +will still occur, these can disturb the kernel while you are +debugging it. The initial cpu will still accept NMI events, +assuming that kdb was not entered for an NMI event. Any cpu +where you use the SS or SSB commands will accept NMI events, even +after the instruction has finished and the cpu is back in kdb. +This is an unavoidable side effect of the fact that doing SS[B] +requires the cpu to drop all the way out of kdb, including +exiting from the NMI event that brought the cpu into kdb. Under +normal circumstances the only NMI event is for the NMI oopser and +that is kdb aware so it does not disturb the kernel while kdb is +running. +.P +Sometimes doing SS or SSB on ix86 will allow one interrupt to proceed, +even though the cpu is disabled for interrupts. I have not been able +to track this one down but I suspect that the interrupt was pending +when kdb was entered and it runs when kdb exits through IRET even +though the popped flags are marked as cli(). If any ix86 hardware +expert can shed some light on this problem, please notify the kdb +maintainer. +.SH EXAMPLES +.nf +.na +.ft CW +kdb> bp gendisk_head datar 4 +Data Access Breakpoint #0 at 0xc024ddf4 (gendisk_head) in dr0 is enabled on cpu 0 +for 4 bytes +kdb> go +... +[root@host /root]# cat /proc/partitions +Entering kdb on processor 0 due to Debug Exception @ 0xc01845e3 +Read/Write breakpoint #0 at 0xc024ddf4 +[0]kdb> ssb +sd_finish+0x7b: movzbl 0xc02565d4,%edx +sd_finish+0x82: leal 0xf(%edx),%eax +sd_finish+0x85: sarl $0x4,%eax +sd_finish+0x88: movl 0xc0256654,%ecx +sd_finish+0x8e: leal (%eax,%eax,4),%edx +sd_finish+0x91: leal (%eax,%edx,2),%edx +sd_finish+0x94: movl 0xc0251108,%eax +sd_finish+0x99: movl %eax,0xffffffc(%ecx,%edx,4) +sd_finish+0x9d: movl %ecx,0xc0251108 +sd_finish+0xa3: xorl %ebx,%ebx +sd_finish+0xa5: cmpb $0x0,0xc02565d4 +[0]kdb> go +[root@host /root]# + +[0]kdb> ss +sys_read: pushl %ebp +SS trap at 0xc01274c1 +sys_read+0x1: movl %esp,%ebp +[0]kdb> ss +sys_read+0x1: movl %esp,%ebp +SS trap at 0xc01274c3 +sys_read+0x3: subl $0xc,%esp +[0]kdb> ss +sys_read+0x3: subl $0xc,%esp +SS trap at 0xc01274c6 +sys_read+0x6: pushl %edi +[0]kdb> + --- /dev/null +++ b/Documentation/kdb/slides @@ -0,0 +1,1382 @@ +#! /opt/cpg/bin/do-mgp +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%deffont "standard" tfont "comic.ttf" +%deffont "thick" tfont "arialb.ttf" +%deffont "typewriter" xfont "courier new-bold-r" +%deffont "type2writer" xfont "arial narrow-bold-r" +%% +%% Default settings per each line numbers. +%% +#%default 1 leftfill, size 2, fore "black", back "LemonChiffon2", font "thick" +%default 1 leftfill, size 2, fore "black", back "white", font "thick" +%default 2 size 10, vgap 10, prefix " ", center +%default 3 size 2, bar "gray70", vgap 10 +%default 4 size 6, fore "black", vgap 30, prefix " ", font "standard", left +%% +%% Default settings that are applied to TAB-indented lines. +%% +%tab 1 size 4, vgap 35, prefix " ", icon arc "red" 40 +%tab 2 size 4, vgap 20, prefix " ", icon delta3 "blue" 40 +%tab 3 size 4, vgap 20, prefix " ", icon delta3 "green" 40 +%% +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +KDB - Kernel Debugger + + + +%size 7,center, font "thick" +Introduction + +And + +Demonstration + + +%size 3 + +February 5, 2002 IBM Linux Technology Center Paul Dorwin +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +IBM Legal + + + IBM Legal requires this information: + +%size 3 + + THE INFORMATION IN THE FOLLOWING PRESENTATION IS PREPARED + SOLELY FOR THE INFORMATION OF THE READER, AND COMES "AS IS" + AND WITHOUT WARRANTY OR REPRESENATION OF ANY KIND. + + ANY PARTY USING THE MATERIALS IN THIS PRESENTATION DOES SO + AT ITS OWN RISK LIABILITY AND THE PROVIDER OF THE MATERIALS + ACCEPTS NO RISK OR LIABILITY FOR SUCH USE OR RESULTING FROM + DISSEMINATION TO OR USE BY ANY OTHER PARTY + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Agenda + +%size 5 + + Installing and Configuring KDB + + KDB Commands + + Scull Demo + + Setting Breakpoints + + Displaying Data Structures + + Kernel Data structures + + Take a walk through an IO operation +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Installing Configuring KDB + + + Install KDB patch. + Start with a clean source tree + Apply architecture specific patches + Obtain patch for your kernel version + see http://oss.sgi.com/projects/kdb/ + Apply the kdb patch + patch -p 1 -N -u -i /path/to/patch + Apply any other patches + Build and reboot on your kdb enabled kernel + Man pages can be found at Documentation/kdb + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Configuring KDB + + + Config kernel with the following options: + These are documented in Documentation/Configure.help + + CONFIG_KDB=y + Enable compilation of KDB in the kernel.. + Setting this also sets CONFIG_KALLSYMS=y. + CONFIG_KDB_MODULES=n + KDB may be extended, compiling kdb/modules. + CONFIG_KDB_OFF=n + y = KDB is disabled by default. + boot with kdb=on to enable at boot. + /proc/sys/kernel/kdb to enable/disable when system is up. + CONFIG_KALLSYMS=y + This causes all symbols to be exported. + CONFIG_FRAME_POINTER=y +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Invoking KDB + + + KDB can be invoked in the following ways: + + Early init with "kdb=early" lilo flag + Hits breakpoint prior to fork_init() (init/main.c) + + Serial console with CNTRL-A + + Console with PAUSE key + + When a pre-set breakpoint is hit + + On panic + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +KDB Commands + + + KDB environment + env Show environment variables + set Set environment variables + help Display Help Message + ? Display Help Message + + System related + sections List kernel and module sections + lsmod List loaded kernel modules + reboot Reboot the machine immediately + cpu Switch to new cpu + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +KDB Commands + + + Memory Manipulation + md Display Memory Contents + mdr Display Raw Memory + mds Display Symbolically + mm Modify Memory Contents + id Display Instructions + + Register Manipulation + rd Display Registers + rm Modify Registers + ef Display exception frame + + Stack + bt [] Stack traceback + btp Display stack for + bta Display all stacks + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +KDB Commands + + + Breakpoint + bc Clear Breakpoint + bd Disable Breakpoint + be Enable Breakpoint + bl [] Display breakpoints + bp [] Set/Display breakpoints + bpa [] Set/Display global breakpoints + bph [] Set hardware breakpoint + bpha [] Set global hardware breakpoint + bp* modifiers: + instruction - break on instruction fetch (default) + datar - break on read at vaddr + dataw - break on write at vaddr + IO - break on in or out op at vaddress + + Execution control + go [] Continue Execution + ss [<#steps>] Single Step + ssb Single step to branch/call +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +KDB Commands + + + Kernel structures + ll Traverse list and execute command + ps Display active task list + vm Display vm_area_struct + dentry Display interesting dentry stuff + filp Display interesting filp stuff + sh Show scsi_host + sd Show scsi_device + sc Show scsi_cmnd + kiobuf Display kiobuf + page Display page + inode Display inode + bh Display buffer head + inode_pages Display pages in an inode +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Scull Demo + + + Objective + Find and display the data associated with a scull device + + The sequence of events + Populate the scull device with data + Identify the breakpoints + Set breakpoint in the device read function + Identify the data structure elements + Identify device structures used to track data + Display data structures containing the data + Show the usage of the filp command + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Scull Demo: Populate Device + + + Obtain the code + Surf to http://examples.oreilly.com/linuxdrive2/ + Download the tarball + Untar it to /usr/src + + Build and install the module + cd /usr/src/ldd2-samples-1.0.1/scull + make + ./scull.init start + + Populate the scull device + cat main.c > /dev/scull0 + cat /dev/scull0 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Scull Demo: Driver Details + + + cat /dev/scull0 + fd = +%fore "blue", cont +open +%fore "black", cont +("/dev/scull0", O_RDONLY); + Kernel finds the file_operations structure + Kernel then invokes the open function +%fore "blue" + read +%fore "black", cont +(fd, buf, size); + Kernel finds the file_operations structure + Kernel then invokes the read function + + Scull device file operations structure + +%font "typewriter", size 3 + struct file_operations scull_fops = { + llseek: scull_llseek, +%fore "blue" + read: scull_read, +%fore "black" + write: scull_write, + ioctl: scull_ioctl, +%fore "blue" + open: scull_open, +%fore "black" + release: scull_release, + }; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Scull Demo: Driver Details + +%font "typewriter", size 3 + scull_open code +%font "typewriter", size 3 + int +%fore "blue", cont +scull_open +%fore "black", cont +(struct inode *inode, struct file *filp) + { + Scull_Dev *dev; /* device information */ + int num = NUM(inode->i_rdev); + + + + dev = (Scull_Dev *)filp->private_data; + if (!dev) { + if (num >= scull_nr_devs) return -ENODEV; +%fore "blue" + dev = &scull_devices[num]; + filp->private_data = dev; +%fore "black" + } + + + + } +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Scull Demo: Driver Details + +%font "typewriter", size 3 + scull_read code +%font "typewriter", size 3 + ssize_t +%fore "blue", cont +scull_read +%fore "black", cont +(struct file *filp, char *buf, size_t count, + loff_t *f_pos) + { + +%fore "blue", cont + Scull_Dev *dev = filp->private_data; +%fore "black", cont + /* the first listitem */ +%fore "blue" + Scull_Dev *dptr; +%fore "black" + int quantum = dev->quantum; + int qset = dev->qset; + int itemsize = quantum * qset; + if (down_interruptible(&dev->sem)) + return -ERESTARTSYS; + if (*f_pos + count > dev->size) + count = dev->size - *f_pos; + + /* find listitem, qset index, and offset in the quantum */ + item = (long)*f_pos / itemsize; + rest = (long)*f_pos % itemsize; + s_pos = rest / quantum; q_pos = rest % quantum; + + /* follow the list up to the right position */ +%fore "blue" + dptr = scull_follow(dev, item); +%fore "black" + + + + } +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Scull Demo: Breakpoints + + +%font "typewriter", size 3 + Determine where to set breakpoint +%font "typewriter", size 3 +%fore "blue" + dptr = scull_follow(dev, item); +%fore "black" + +%font "typewriter", size 3 + Disassemble scull_read +%font "typewriter", size 3 + [0]kdb> +%fore "blue", cont +id scull_read +%fore "black" + 0xf8c083b4 scull_read: push %ebp + 0xf8c083b5 scull_read+0x1:mov %esp,%ebp + 0xf8c083b7 scull_read+0x3:push %edi + + 0xf8c08465 scull_read+0xb1:sub $0x8,%esp +%fore "blue" + 0xf8c08468 scull_read+0xb4:push %ecx + 0xf8c08469 scull_read+0xb5:push %esi + 0xf8c0846a scull_read+0xb6:call 0xf8c08364 scull_follow: +%fore "black" + 0xf8c0846f scull_read+0xbb:mov %eax, +%fore "blue", cont + %edx +%fore "black" + 0xf8c08471 +%fore "blue", cont +scull_read+0xbd +%fore "black", cont +:add $0x10,%esp + + + Set breakpoint in driver read +%font "typewriter", size 3 + [0]kdb> +%fore "blue",cont +bp scull_read+0xbd +%fore "black" + Instruction(i) BP #0 at 0xf8c08471 ([scull]scull_read+0xbd) + is enabled globally adjust 1 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Scull Demo: Breakpoints + + +%font "typewriter", size 3 + Restart the system +%font "typewriter", size 3 + [0]kdb> +%fore "blue", cont +go +%fore "black" + + Hit the Breakpoint +%font "typewriter", size 3 + [root@elm3b77 root]# +%fore "blue", cont +cat /dev/scull0 +%fore "black" + Instruction(i) breakpoint #0 at 0xf8c08471 (adjusted) + 0xf8c08471 scull_read+0xbd:int3 + Entering kdb (current=0xf73ec000, pid 1249) on processor 2 + due to Breakpoint @ 0xf8c08471 + + Display the registers +%font "typewriter", size 3 + [2]kdb> +%fore "blue", cont +rd +%fore "black" + eax = 0xf77d7b60 ebx = 0x00000000 ecx = 0x00000000 edx = +%fore "blue", cont +0xf77d7b60 +%fore "black" + esi = +%fore "blue", cont +0xf77d7b60 +%fore "black", cont + edi = 0x00001000 esp = 0xf7415f40 eip = 0xf8c08471 + ebp = 0xf7415f78 xss = 0x00000018 xcs = 0x00000010 eflags = 0x00000246 + xds = 0xf7590018 xes = 0x00000018 origeax = 0xffffffff ®s = 0xf7415f0c +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Scull Demo: Data Structures + +%font "typewriter", size 3 + Display the Scull_Dev structure +%font "typewriter", size 3 + [2]kdb> +%fore "blue", cont +md 0xf77d7b60 2 +%fore "black" + 0xf77d7b60 +%fore "blue", cont +f7400000 +%fore "black", cont + 00000000 00000fa0 000003e8 ..@w.... ...h... + 0xf77d7b70 0000534e 00000000 00000000 00000000 NS.............. + + Scull Device Structure +%font "typewriter", size 3 + typedef struct Scull_Dev { +%fore "blue" + void **data; +%fore "black" + struct Scull_Dev *next; /* next listitem */ + int quantum; /* the current quantum size */ + int qset; /* the current array size */ + unsigned long size; + devfs_handle_t handle; /* only used if devfs is there */ + unsigned int access_key; /* used by sculluid and scullpriv */ + struct semaphore sem; /* mutual exclusion semaphore */ + } Scull_Dev; +%size 6 + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Scull Demo: Data Structures + + +%font "typewriter", size 3 + Display the quantum set (dev->data) +%font "typewriter", size 3 + [2]kdb> +%fore "blue", cont +md f7400000 2 +%fore "black" + 0xf7400000 +%fore "blue", cont +f73ea000 +%fore "black", cont + f73f1000 f740c000 f7ab4000 . >w..?w.@@w.@+w + 0xf7400010 f73ef000 f755b000 00000000 00000000 .p>w.0Uw........ + + Display the first quantum (dev->data[0]) +%font "typewriter", size 3 + [2]kdb> +%fore "blue", cont +md f73ea000 +%fore "black" + 0xf73ea000 200a2a2f 616d202a 632e6e69 202d2d20 /*. * main.c -- + 0xf73ea010 20656874 65726162 75637320 63206c6c the bare scull c + 0xf73ea020 20726168 75646f6d 200a656c 2a200a2a har module. *. * + 0xf73ea030 706f4320 67697279 28207468 32202943 Copyright (C) 2 + 0xf73ea040 20313030 73656c41 646e6173 52206f72 001 Alessandro R + 0xf73ea050 6e696275 6e612069 6f4a2064 6874616e ubini and Jonath + 0xf73ea060 43206e61 6562726f 2a200a74 706f4320 an Corbet. * Cop + 0xf73ea070 67697279 28207468 32202943 20313030 yright (C) 2001 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Scull Demo: filp command + + +%font "typewriter", size 3 + Show filp usage - here is the scull_read prototype +%font "typewriter", size 3 + ssize_t scull_read( +%fore "blue", cont +struct file *filp +%fore "black", cont +, char *buf, + size_t count, loff_t *f_pos); + Show the stack trace: +%font "typewriter", size 3 +[2]kdb> +%fore "blue", cont +bt +%fore "black" + EBP EIP Function(args) + 0xee9dbf78 0xf8c08471 [scull]scull_read+0xbd ( +%fore "blue", cont +0xeaf6c0c0 +%fore "black", cont +, 0x804e128, + 0x1000, 0xeaf6c0e0, 0x804f000) + scull .text 0xf8c08060 0xf8c083b4 0xf8c084dc + 0xee9dbfbc 0xc0136278 sys_read+0x98 (0x3, 0x804e128, 0x1000, ... + kernel .text 0xc0100000 0xc01361e0 0xc01362b0 + 0xc010702b system_call+0x33 + kernel .text 0xc0100000 0xc0106ff8 0xc0107030 + And show the filp output +%font "typewriter", size 3 + [2]kdb> +%fore "blue", cont +filp 0xeaf6c0c0 +%fore "black" + name.name 0xe93889fc name.len 6 + File Pointer at 0xeaf6c0c0 + f_list.nxt = 0xe42deca0 f_list.prv = 0xf7e69070 +%fore "blue" + f_dentry = 0xe93889a0 +%fore "black", cont + f_op = 0xf8c0a200 + f_count = 2 f_flags = 0x8000 f_mode = 0x1 + f_pos = 0 f_reada = 0 f_ramax = 0 + f_raend = 0 f_ralen = 0 f_rawin = 0 + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Scull Demo: filp command + + +%font "typewriter", size 3 + filp output - continued +%font "typewriter", size 3 +%fore "blue" + Directory Entry at 0xe93889a0 +%fore "black" + d_name.len = 6 +%fore "orange", cont +d_name.name = 0xe93889fc +%fore "black", cont +> + d_count = 1 d_flags = 0x0 +%fore "blue", cont +d_inode = 0xe827b680 +%fore "black" + d_hash.nxt = 0xc215aec8 d_hash.prv = 0xc215aec8 + d_lru.nxt = 0xe93889b8 d_lru.prv = 0xe93889b8 + d_child.nxt = 0xe89e1e80 d_child.prv = 0xe9388940 + d_subdirs.nxt = 0xe93889c8 d_subdirs.prv = 0xe93889c8 + d_alias.nxt = 0xe827b690 d_alias.prv = 0xe827b690 + d_op = 0x00000000 d_sb = 0xf7e69000 + +%fore "blue" + Inode Entry at 0xe827b680 +%fore "black" + i_mode = 0x21a4 i_nlink = 1 i_rdev = 0xfe00 + i_ino = 37182 i_count = 1 i_dev = 0x821 + i_hash.nxt = 0xc20e6be8 i_hash.prv = 0xc20e6be8 + i_list.nxt = 0xe827b2c8 i_list.prv = 0xe827b868 + i_dentry.nxt = 0xe93889d0 i_dentry.prv = 0xe93889d0 + + Check the filename (display d_name.name) +%font "typewriter", size 3 + [2]kdb> +%fore "orange", cont +md 0xe93889fc 1 +%fore "black" + 0xe93889fc 6c756373 0000306c 00000000 00000000 scull0.......... + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Kernel Structures + + + Objective + Show output from various kernel related kdb commands + + Sequence of events + Simple Program + Write a simple program which allocates memory and hangs + Show usage of the ps, vm, and ll commands + Walk an IO operation + Hit a breakpoint in qlogic driver (isp1020_queuecommand) + Show usage of scsi related commands (sc, sh, and sd) + Show usage of vm related commands (bh, page, inode, inode_pages) + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Simple program + +%font "typewriter", size 3 + simple.c - simple program which allocates memory +%font "typewriter", size 3 +%fore "blue" + int foo_global[8192]; +%fore "black" + main() + { + int * +%fore "blue", cont +foo_malloc; +%fore "black" + int i; + foo_malloc = (int *)malloc(0x8192); + for(i = 0; i < 0x100; i++) { + foo_global[i] = 0xdead0000 | i; + foo_malloc[i] = 0xbeef0000 | i; + } + printf("foo_global at %x\n", (int)foo_global); + printf("foo_malloc at %x\n", (int)foo_malloc); + printf("sleep forever\n"); + sleep(2000000); + } + + simple output +%font "typewriter", size 3 + [root@elm3b77 scull]# cc -o simple simple.c + [root@elm3b77 scull]# ./simple + foo_global at +%fore "blue", cont +8049780 +%fore "black" + foo_malloc at +%fore "blue", cont +8051788 +%fore "black" + sleep forever + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Simple Program + +%font "typewriter", size 3 + Show the output of the ps command +%font "typewriter", size 3 + Entering kdb (current=0xc2010000, pid 0) on processor 3 due to + Keyboard Entry + [3]kdb> +%fore "blue", cont +ps +%fore "black" + Task Addr Pid Parent [*] cpu State Thread Command + 0xf7efe000 00000001 00000000 0 003 stop 0xf7efe370 init + 0xf7ef0000 00000002 00000001 0 001 stop 0xf7ef0370 keventd + 0xf7eec000 00000003 00000000 0 000 stop 0xf7eec370 ksoftirqd_CPU0 + 0xf7eea000 00000004 00000000 0 001 stop 0xf7eea370 ksoftirqd_CPU1 + 0xf7ee8000 00000005 00000000 0 002 stop 0xf7ee8370 ksoftirqd_CPU2 + 0xf7ee6000 00000006 00000000 0 003 stop 0xf7ee6370 ksoftirqd_CPU3 + + + + 0xf7b46000 00001006 00000737 0 003 stop 0xf7b46370 sshd + 0xf7ace000 00001007 00001006 0 000 stop 0xf7ace370 bash + 0xef06a000 00001066 00001007 0 003 stop 0xef06a370 su + 0xeef88000 00001067 00001066 0 000 stop 0xeef88370 bash + 0xeef64000 00001119 00000770 0 001 stop 0xeef64370 in.ftpd +%fore "blue" + 0xeeeac000 +%fore "black", cont + 00001138 00001067 0 001 stop 0xeeeac370 +%fore "blue", cont +simple +%fore "black" + [3]kdb> +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Simple Program + +%font "typewriter", size 3 + Display the task struct +%font "typewriter", size 3 + [3]kdb> +%fore "blue", cont +md 0xeeeac000 +%fore "black" + 0xeeeac000 00000001 00000000 00000000 c0000000 ................ + 0xeeeac010 c0339880 00000000 00000000 ffffffff ................ + 0xeeeac020 0000000a 00000000 00000000 +%fore "blue", cont +f7e10f00 +%fore "black", cont + ..............aw + 0xeeeac030 00000001 ffffffff ffffffff 00000000 ................ + +%font "typewriter", size 3 + Determine offset of mm_struct ptr in task_struct +%font "typewriter", size 3 + struct task_struct { + [0] volatile long state; + [4] unsigned long flags; + [8] int sigpending; + [c] mm_segment_t addr_limit; + [10] struct exec_domain *exec_domain; + [14] volatile long need_resched; + [18] unsigned long ptrace; + [1c] int lock_depth; + [20] long counter; + [24] long nice; + [28] unsigned long policy; +%fore "blue" + [2c] struct mm_struct *mm; +%fore "black" + [30] int processor; + [34] unsigned long cpus_runnable, cpus_allowed; + + }; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Simple Program + + +%font "typewriter", size 3 + Display the mm_struct associated with simple process +%font "typewriter", size 3 + [3]kdb> +%fore "blue", cont +md f7e10f00 +%fore "black" + 0xf7e10f00 +%fore "blue", cont +e8357a80 +%fore "black", cont + e8357978 f7ac77e0 eb15eac0 .z5hxy5h`w,w@j.k + 0xf7e10f10 00000001 00000002 0000000b 00000000 ................ + 0xf7e10f20 00000001 f7e10f24 f7e10f24 00000001 ................ + 0xf7e10f30 f7e35e70 eea7e8f0 08048000 0804862b ................ + 0xf7e10f40 0804962c 08049744 08051780 0805a000 ................ + 0xf7e10f50 bffffd10 bffffe00 bffffe09 bffffe09 ................ + 0xf7e10f60 bffffff3 0000005a 00000168 00000000 ................ + 0xf7e10f70 00000000 00000002 00000000 00000001 ................ + +%font "typewriter", size 3 + Determine offset of the first vma in the process +%font "typewriter", size 3 + struct mm_struct { +%fore "blue" + struct vm_area_struct * mmap; +%fore "black" + rb_root_t mm_rb; + struct vm_area_struct * mmap_cache; + + }; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Simple Program + +%font "typewriter", size 3 + Display the first vma using md +%font "typewriter", size 3 + [3]kdb> +%fore "blue", cont +md e8357a80 +%fore "black" + 0xe8357a80 f7e10f00 08048000 08049000 +%fore "blue", cont +e8727e00 +%fore "black",cont + ..aw.........~rh + 0xe8357a90 00000025 00001875 e8727e18 00000001 %...u....~rh.... + + Display the first vma using vma +%font "typewriter", size 3 + [3]kdb> +%fore "blue", cont +vma e8357a80 +%fore "black" + struct vm_area_struct at 0xe8357a80 for 68 bytes + vm_start = 0x8048000 vm_end = 0x8049000 + page_prot = 0x25 + flags: READ EXEC MAYREAD MAYWRITE MAYEXEC DENYWRITE EXECUTABLE +%font "typewriter", size 3 + + Determine the offset to the vma list +%font "typewriter", size 3 + struct vm_area_struct { + [0] struct mm_struct * vm_mm; + [4] unsigned long vm_start; + [8] unsigned long vm_end; +%fore "blue" + [c] struct vm_area_struct *vm_next; +%fore "black" + + }; + Display the next vma +%font "typewriter", size 3 + [3]kdb> vma e8727e00 + struct vm_area_struct at 0xe8727e00 for 68 bytes + vm_start = 0x8049000 vm_end = 0x804a000 + page_prot = 0x25 + flags: READ WRITE MAYREAD MAYWRITE MAYEXEC DENYWRITE EXECUTABLE +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Simple Program + +%font "typewriter", size 3 + Use the ll command to display the list of vma's +%font "typewriter", size 3 + [3]kdb> ll e8357a80 0xc vma +. + struct vm_area_struct at 0xe8357a80 for 68 bytes + vm_start = 0x8048000 vm_end = 0x8049000 + page_prot = 0x25 + flags: READ EXEC MAYREAD MAYWRITE MAYEXEC DENYWRITE EXECUTABLE +. + struct vm_area_struct at 0xe8727e00 for 68 bytes + vm_start = +%fore "orange", cont +0x8049000 +%fore "black", cont + vm_end = +%fore "orange", cont +0x804a000 +%fore "black" + page_prot = 0x25 + flags: READ WRITE MAYREAD MAYWRITE MAYEXEC DENYWRITE EXECUTABLE +. + struct vm_area_struct at 0xe8727c80 for 68 bytes + vm_start = +%fore "blue", cont +0x804a000 +%fore "black", cont + vm_end = +%fore "blue", cont +0x805a000 +%fore "black" + page_prot = 0x25 + flags: READ WRITE EXEC MAYREAD MAYWRITE MAYEXEC + + struct vm_area_struct at 0xe8357900 for 68 bytes + vm_start = 0xbfffe000 vm_end = 0xc0000000 + page_prot = 0x25 + flags: READ WRITE EXEC MAYREAD MAYWRITE MAYEXEC GROWSDOWN + + Match the vma to the displayed addresses +%font "typewriter", size 3 + foo_global at +%fore "orange", cont +8049780 +%fore "black" + foo_malloc at +%fore "blue", cont +8051788 +%fore "black" +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Walking IO structures + + + Objective + Show usage of various scsi and vm related kdb commands + + Sequence: + Set a breakpoint in the scsi driver + Stops when queueing a command to the controller + Cause IO on an idle disk + Show various IO stack traces + Display the IO data structures + Display vm information about the data + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Walking IO structures + + +%font "typewriter", size 3 + Set the breakpoint + +%font "typewriter", size 3 + [3]kdb> +%fore "blue", cont +bp isp1020_queuecommand +%fore "black" + Instruction(i) BP #0 at 0xc01ecfe0 (isp1020_queuecommand) + is enabled globally adjust 1 + +%font "typewriter", size 3 + Create some activity on a previously unused disk + +%font "typewriter", size 3 + [3]kdb> +%fore "blue", cont +go +%fore "black" + [root@elm3b77 root]# +%fore "blue", cont +ls /rh62 +%fore "black" + + Instruction(i) breakpoint #0 at 0xc01ecfe0 (adjusted) + 0xc01ecfe0 isp1020_queuecommand:int3 + + Entering kdb (current=0xf75ba000, pid 1181) on processor 3 due to + Breakpoint @ 0xc01ecfe0 + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Walking IO structures + + +%font "typewriter", size 3 + Show the stack. + This is a read of the /rh62 directory + +%font "typewriter", size 3 + [1]kdb> +%fore "blue", cont +bt +%fore "black" + EBP EIP Function(args) + 0xf75bbdf4 0xc01ecfe0 isp1020_queuecommand + 0xc01e2c77 scsi_dispatch_cmd+0x1f7 + 0xf75bbe24 0xc01e99b1 scsi_request_fn+0x2f1 + 0xf75bbe34 0xc01c84fd generic_unplug_device+0x2d + 0xf75bbe50 0xc011b3af __run_task_queue+0x5f + 0xf75bbe6c 0xc013a63c block_sync_page+0x1c + 0xf75bbe98 0xc0128127 __lock_page+0x77 + 0xf75bbea4 0xc0128178 lock_page+0x18 + 0xf75bbec8 0xc012a4b3 read_cache_page+0xc3 + 0xf75bbef4 0xc0168e23 ext2_get_page+0x23 + 0xf75bbf48 0xc0168fdd ext2_readdir+0xfd + 0xf75bbf68 0xc0143d2e vfs_readdir+0x7e + 0xf75bbfbc 0xc01442ed +%fore "blue", cont +sys_getdents64+0x4d +%fore "black" + 0xc010702b system_call+0x33 + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Walking IO structures + + +%font "typewriter", size 3 + Allow the operation to complete + +%font "typewriter", size 3 + [3]kdb> +%fore "blue", cont +go +%fore "black" + bench build etc lib mnt oldsys rh72 spv usr + bin data h linux mnt1 opt root test var + boot dev home lost+found mnt2 proc sbin tmp + +%font "typewriter", size 3 + Force some more activity + +%font "typewriter", size 3 + [root@elm3b77 root]# +%fore "blue", cont +cd /rh62/tmp +%fore "black" + Instruction(i) breakpoint #0 at 0xc01ecfe0 (adjusted) + 0xc01ecfe0 isp1020_queuecommand:int3 + + Entering kdb (current=0xf768a000, pid 981) on processor 3 due to + Breakpoint @ 0xc01ecfe0 + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Walking IO structures + + +%font "typewriter", size 3 + Show the stack. + This is an inode read for /rh62/tmp + +%font "typewriter", size 3 + [3]kdb> +%fore "blue", cont +bt +%fore "black" + EBP EIP Function(args) + 0xf768bd68 0xc01ecfe0 isp1020_queuecommand + 0xc01e2c77 scsi_dispatch_cmd+0x1f7 + 0xf768bd98 0xc01e99b1 scsi_request_fn+0x2f1 + 0xf768bda8 0xc01c84fd generic_unplug_device+0x2d + 0xf768bdc4 0xc011b3af __run_task_queue+0x5f + 0xf768bdfc 0xc0137216 __wait_on_buffer+0x56 + 0xf768be1c 0xc0138600 bread+0x50 + 0xf768be5c 0xc016b684 ext2_read_inode+0x114 + 0xf768bf0c 0xc013fbec real_lookup+0x7c + 0xf768bf78 0xc014035d link_path_walk+0x5ad +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Walking IO structures + + +%font "typewriter", size 3 + Create a new file, causing yet more disk activity + +%font "typewriter", size 3 + [3]kdb> +%fore "blue", cont +go +%fore "black" + + [root@elm3b77 tmp]# +%fore "blue", cont +echo "Hello linux reading group" > j1;sync +%fore "black" + + Instruction(i) breakpoint #0 at 0xc01ecfe0 (adjusted) + 0xc01ecfe0 isp1020_queuecommand:int3 + + Entering kdb (current=0xf768a000, pid 981) on processor 3 due to + Breakpoint @ 0xc01ecfe0 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Walking IO structures + + +%font "typewriter", size 3 + Show the stack + This is an inode read in response to the open +%font "typewriter", size 3 + [3]kdb> +%fore "blue", cont +bt +%fore "black" + EBP EIP Function(args) + 0xf768bd78 0xc01ecfe0 isp1020_queuecommand + 0xc01e2c77 scsi_dispatch_cmd+0x1f7 + 0xf768bda8 0xc01e99b1 scsi_request_fn+0x2f1 + 0xf768bdb8 0xc01c84fd generic_unplug_device+0x2d + 0xf768bdd4 0xc011b3af __run_task_queue+0x5f + 0xf768bdf0 0xc013a63c block_sync_page+0x1c + 0xf768be1c 0xc0128127 __lock_page+0x77 + 0xf768be28 0xc0128178 lock_page+0x18 + 0xf768be4c 0xc012a4b3 read_cache_page+0xc3 + 0xf768be78 0xc0168e23 ext2_get_page+0x23 + 0xf768beb8 0xc01691ed ext2_find_entry+0x8d + 0xf768bed4 0xc016933a ext2_inode_by_name+0x1a + 0xf768befc 0xc016c077 ext2_lookup+0x27 + 0xf768bf1c 0xc014094a lookup_hash+0x9a + 0xf768bf64 0xc0140c4d open_namei+0xfd + 0xf768bfa0 0xc0135907 filp_open+0x37 + 0xf768bfbc 0xc0135c64 sys_open+0x34 + 0xc010702b system_call+0x33 + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Walking IO structures + + +%font "typewriter", size 3 + Let the operation continue +%font "typewriter", size 3 + [3]kdb> +%fore "blue", cont +go +%fore "black" + Instruction(i) breakpoint #0 at 0xc01ecfe0 (adjusted) + 0xc01ecfe0 isp1020_queuecommand: int3 + Entering kdb (current=0xc0352000, pid 0) on processor 0 due to + Breakpoint @ 0xc01ecfe0 + Show the stack + This is an io completion queuing the next request +%font "typewriter", size 3 + [0]kdb> +%fore "blue", cont +bt +%fore "black" + EBP EIP Function(args) + 0xc0353df4 0xc01ecfe0 isp1020_queuecommand( +%fore "blue", cont +0xf7e63a00 +%fore "black", cont +,0xc01e7fc0... + 0xc01e2c77 scsi_dispatch_cmd+0x1f7 + 0xc0353e24 0xc01e99b1 scsi_request_fn+0x2f1 + 0xc0353e40 0xc01e8f6a +%fore "blue", cont +scsi_queue_next_request+0x4a +%fore "black" + 0xc0353e5c 0xc01e9166 __scsi_end_request+0x116 + 0xc0353ea8 0xc01e93e0 +%fore "blue", cont +scsi_io_completion+0x170 +%fore "black" + 0xc0353ecc 0xc01f658e rw_intr+0x14e + 0xc0353ef8 0xc01e8668 scsi_old_done+0x6a8 + 0xc0353fd4 0xc01052c2 cpu_idle+0x52 + Function prototype +%font "typewriter", size 3 + int isp1020_queuecommand( +%fore "blue", cont +Scsi_Cmnd *Cmnd, +%fore "black" + void (*done)(Scsi_Cmnd *)) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Walking IO structures + + +%font "typewriter", size 3 + Show the command being queued +%font "typewriter", size 3 + [0]kdb> +%fore "blue", cont +sc 0xf7e63a00 +%fore "black" + scsi_cmnd at 0xf7e63a00 +%fore "blue" + host = 0xf7e91400 +%fore "black", cont + state = 4099 owner = 258 +%fore "blue", cont +device = 0xf7ed5d80 +%fore "black" + bnext = 0x00000000 reset_chain = 0x00000000 eh_state = 0 + done = 0xc01f6440 + serial_number = 3402 serial_num_at_to = 0 retries = 0 timeout = 0 + id/lun/cmnd = [0/0/0] cmd_len = 10 old_cmd_len = 10 + cmnd = [2a/00/00/28/00/3f/00/00/10/00/ef/f7] + data_cmnd = [2a/00/00/28/00/3f/00/00/10/00/ef/f7] + request_buffer = 0xc03fd000 bh_next = 0x00000000 + request_bufflen = 8192 + use_sg = 2 old_use_sg = 2 sglist_len = 512 abore_reason = 0 + bufflen = 8192 buffer = 0xc03fd000 underflow = 8192 + transfersize = 512 + tag = 0 pid = 3401 + request struct + rq_status = RQ_ACTIVE rq_dev = [8/1] errors = 1 cmd = 0 + sector = 2621440 nr_sectors = 16 current_nr_sectors = 8 + buffer = 0xf7599000 +%fore "blue", cont +bh = 0xf75ca300 +%fore "black", cont + bhtail = 0xf75ca3c0 + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Walking IO structures + + +%font "typewriter", size 3 + Display the host adapter +%font "typewriter", size 3 + [0]kdb> +%fore "blue", cont +sh 0xf7e91400 +%fore "black" + Scsi_Host at 0xf7e91400 + next = 0x00000000 +%fore "blue", cont +host_queue = 0xf7ed5d80 +%fore "black" + ehandler = 0x00000000 eh_wait = 0x00000000 en_notify = 0x00000000 + eh_action = 0x00000000 + h_active = 0x0 host_wait = 0xc0353ac4 hostt = 0xc034bce0 + host_busy = 1 + host_failed = 0 extra_bytes = 524 host_no = 0 resetting = 0 + max id/lun/channel = [16/8/0] this_id = 7 + can_queue = 64 cmd_per_lun = 1 sg_tablesize = 427 u_isa_dma = 0 + host_blocked = 0 reverse_ordering = 0 + +%font "typewriter", size 3 + Display the scsi device +%font "typewriter", size 3 + [0]kdb> +%fore "blue", cont +sd 0xf7ed5d80 +%fore "black" + scsi_device at 0xf7ed5d80 + next = 0xf7ed5c80 prev = 0x00000000 host = 0xf7e91400 + device_busy = 1 +%fore "blue", cont +device_queue 0xf7e63a00 +%fore "black" + id/lun/chan = [0/0/0] single_lun = 0 device_blocked = 0 + queue_depth = 1 current_tag = 0 scsi_level = 4 + IBM DGHS18X 0360 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Walking IO structures + + +%font "typewriter", size 3 + Display the Buffer header associated with the command +%font "typewriter", size 3 + [0]kdb> +%fore "blue", cont +bh 0xf75ca300 +%fore "black" + buffer_head at 0xf75ca300 + next 0x00000000 bno 327680 rsec 2621440 size 4096 + dev 0x801 rdev 0x801 + count 2 state 0x1d [Uptodate Lock Req Mapped] ftime 0x7695e + b_list 1 b_reqnext 0xf75ca3c0 b_data 0xf7599000 +%fore "blue" + b_page 0xc1dd6640 +%fore "black", cont + b_this_page 0xf75ca300 b_private 0x00000000 + + Display the associated page structure +%font "typewriter", size 3 + [0]kdb> +%fore "blue", cont +page 0xc1dd6640 +%fore "black" + struct page at 0xc1dd6640 + next 0xc1dd7300 prev 0xc1dd6240 +%fore "blue", cont +addr space 0xf7af04d0 +%fore "black" + index 327680 (offset 0x50000000) + count 2 flags PG_referenced PG_lru virtual 0xf7599000 + buffers 0xf75ca300 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Walking IO structures + + +%font "typewriter", size 3 + Display the Address space associated with the page +%font "typewriter", size 3 + [0]kdb> +%fore "blue", cont +md 0xf7af04d0 +%fore "black" + 0xf7af04d0 c1dd6240 c1dea740 f7af04d8 f7af04d8 @b]A@'^AX./wX./w + 0xf7af04e0 f7af04e0 f7af04e0 00000007 c033b700 `./w`./w.....73@ + 0xf7af04f0 +%fore "blue", cont +f7af0420 +%fore "black", cont + 00000000 00000000 00000001 ./w............ + 0xf7af0500 000001d0 00000000 00000000 f7af050c P............./w + 0xf7af0510 f7af050c 00000000 f7a8afa0 00000000 ../w.... /(w.... + + The structure looks like: +%size 3 + struct address_space { + struct list_head clean_pages; /* list of clean pages */ + struct list_head dirty_pages; /* list of dirty pages */ + struct list_head locked_pages;/* list of locked pages */ + unsigned long nrpages; /* number of total pages */ + spinlock_t page_lock; /* spinlock protecting them*/ + struct address_space_operations *a_ops; /* methods */ +%fore "blue" + struct inode *host; /* owner: inode, block_dev */ +%fore "black" + + }; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Walking IO structures + + +%font "typewriter", size 3 + Display the inode associated with the address space + I think htis is the inode for the block device. + +%font "typewriter", size 3 + [1]kdb> +%fore "blue", cont +inode f7af0420 +%fore "black" + struct inode at 0xf7af0420 + i_ino = 289 i_count = 1 i_dev = 0x801 i_size 4301789184 + i_mode = 0x8000 i_nlink = 1 i_rdev = 0x801 + i_hash.nxt = 0xf7af0420 i_hash.prv = 0xf7af0420 + i_list.nxt = 0xf7af0608 i_list.prv = 0xf7af0068 + i_dentry.nxt = 0xf7af0430 i_dentry.prv = 0xf7af0430 + i_dirty_buffers.nxt = 0xf7af0438 i_dirty_buffers.prv = 0xf7af0438 + i_sb = 0xc201f200 i_op = 0xc03cfdc0 i_data = 0xf7af04d0 nrpages = 6 + i_mapping = 0xf7af04d0 + i_flags 0x0 i_state 0x0 [] fs specific info @ 0xf7af0540 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%page + +Walking IO structures + + +%font "typewriter", size 3 + Display the page list associated with the inode +%font "typewriter", size 3 + [0]kdb> +%fore "blue", cont +inode_pages f7af0420 +%fore "black" +CLEAN page_struct index cnt flags + 0xc1dd6240 327735 2 0x44 bh 0xf75caae0 bno 327735 + [Lock Req Mapped] +%fore "blue" + 0xc1dd6640 327680 2 0x44 bh 0xf75ca300 bno 327680 + [Uptodate Lock Req Mapped] +%fore "black" + 0xc1dd7300 327681 2 0x44 bh 0xf75ca3c0 bno 327681 + [Uptodate Lock Req Mapped] + 0xc1dd6e00 327684 2 0x44 bh 0xf75ca420 bno 327684 + [Uptodate Req Mapped] + 0xc1de8fc0 4 2 0xc0 bh 0xf7b5ade0 bno 4 + [Uptodate Req Mapped] + 0xc1dea700 1 2 0x44 bh 0xf7e02740 bno 1 + [Uptodate Req Mapped] + 0xc1dea740 0 2 0x44 bh 0xf7e028c0 bno 0 + [Uptodate Req Mapped] +DIRTY page_struct index cnt flags +LOCKED page_struct index cnt flags --- a/Makefile +++ b/Makefile @@ -672,6 +672,7 @@ export mod_strip_cmd ifeq ($(KBUILD_EXTMOD),) core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ +core-$(CONFIG_KDB) += kdb/ vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \ $(core-y) $(core-m) $(drivers-y) $(drivers-m) \ --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -43,6 +43,9 @@ #include #include #include +#ifdef CONFIG_KDB +#include +#endif /* CONFIG_KDB */ extern void ctrl_alt_del(void); @@ -1199,6 +1202,13 @@ static void kbd_keycode(unsigned int key } #endif +#ifdef CONFIG_KDB + if (down && !rep && keycode == KEY_PAUSE && kdb_on == 1) { + kdb(KDB_REASON_KEYBOARD, 0, get_irq_regs()); + return; + } +#endif /* CONFIG_KDB */ + #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */ if (keycode == KEY_SYSRQ && (sysrq_down || (down == 1 && sysrq_alt))) { if (!sysrq_down) { --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -44,6 +44,10 @@ #define DRIVER_DESC "USB HID core driver" #define DRIVER_LICENSE "GPL" +#ifdef CONFIG_KDB_USB +#include +#endif + /* * Module parameters. */ @@ -1032,6 +1036,34 @@ static int usbhid_start(struct hid_devic USB_INTERFACE_PROTOCOL_KEYBOARD) usbhid_set_leds(hid); +#ifdef CONFIG_KDB_USB + /* Attach USB keyboards to kdb */ + if (intf->cur_altsetting->desc.bInterfaceProtocol == + USB_INTERFACE_PROTOCOL_KEYBOARD) { + int ret; + struct usbhid_device *usbhid = hid->driver_data; + extern void *usb_hcd_get_kdb_poll_func(struct usb_device *udev); + extern void * usb_hcd_get_kdb_completion_func(struct usb_device *udev); + extern int usb_hcd_check_uhci(struct usb_device *udev); + extern kdb_hc_keyboard_attach_t + usb_hcd_get_hc_keyboard_attach(struct usb_device *udev); + extern kdb_hc_keyboard_detach_t + usb_hcd_get_hc_keyboard_detach(struct usb_device *udev); + + ret = kdb_usb_keyboard_attach(usbhid->urbin, usbhid->inbuf, + usb_hcd_get_kdb_poll_func(interface_to_usbdev(intf)), + usb_hcd_get_kdb_completion_func(interface_to_usbdev(intf)), + usb_hcd_get_hc_keyboard_attach(interface_to_usbdev(intf)), + usb_hcd_get_hc_keyboard_detach(interface_to_usbdev(intf)), + usbhid->bufsize, + NULL); + + if (ret == -1) + printk(": FAILED to register keyboard (%s) " + "with KDB\n", hid->phys); + } +#endif /* CONFIG_KDB_USB */ + return 0; fail: @@ -1051,6 +1083,14 @@ static void usbhid_stop(struct hid_devic if (WARN_ON(!usbhid)) return; +#ifdef CONFIG_KDB_USB + /* + * If the URB was for a Keyboard, detach it from kdb. + * If the URB was for another type of device, just + * allow kdb_usb_keyboard_detach() to silently fail. + */ + kdb_usb_keyboard_detach(usbhid->urbin); +#endif clear_bit(HID_STARTED, &usbhid->iofl); spin_lock_irq(&usbhid->lock); /* Sync with error handler */ --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -30,6 +30,9 @@ #include #include #include +#ifdef CONFIG_KDB_USB +#include +#endif /* * Version Information @@ -292,6 +295,16 @@ static int usb_kbd_probe(struct usb_inte usb_fill_int_urb(kbd->irq, dev, pipe, kbd->new, (maxp > 8 ? 8 : maxp), usb_kbd_irq, kbd, endpoint->bInterval); + +#ifdef CONFIG_KDB_USB + /* Attach keyboard to kdb */ + extern void * usb_hcd_get_kdb_poll_func(struct usb_device *udev); + + kdb_usb_keyboard_attach(kbd->irq, kbd->new, + usb_hcd_get_kdb_poll_func(dev)); + +#endif /* CONFIG_KDB_USB */ + kbd->irq->transfer_dma = kbd->new_dma; kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; @@ -329,6 +342,10 @@ static void usb_kbd_disconnect(struct us usb_set_intfdata(intf, NULL); if (kbd) { +#ifdef CONFIG_KDB_USB + /* Detach the keyboard from kdb */ + kdb_usb_keyboard_detach(kbd->irq); +#endif /* CONFIG_KDB_USB */ usb_kill_urb(kbd->irq); input_unregister_device(kbd->dev); usb_kbd_free_mem(interface_to_usbdev(intf), kbd); --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -44,11 +44,26 @@ #include #include "8250.h" - #ifdef CONFIG_SPARC #include "suncore.h" #endif +#ifdef CONFIG_KDB +#include +/* + * kdb_serial_line records the serial line number of the first serial console. + * NOTE: The kernel ignores characters on the serial line unless a user space + * program has opened the line first. To enter kdb before user space has opened + * the serial line, you can use the 'kdb=early' flag to lilo and set the + * appropriate breakpoints. + */ + +static int kdb_serial_line = -1; +static const char *kdb_serial_ptr = kdb_serial_str; +#else +#define KDB_8250() 0 +#endif /* CONFIG_KDB */ + /* * Configuration: * share_irqs - whether we pass IRQF_SHARED to request_irq(). This option @@ -1403,6 +1418,20 @@ receive_chars(struct uart_8250_port *up, * just force the read character to be 0 */ ch = 0; +#ifdef CONFIG_KDB + if ((up->port.line == kdb_serial_line) && kdb_on == 1) { + if (ch == *kdb_serial_ptr) { + if (!(*++kdb_serial_ptr)) { + atomic_inc(&kdb_8250); + kdb(KDB_REASON_KEYBOARD, 0, get_irq_regs()); + atomic_dec(&kdb_8250); + kdb_serial_ptr = kdb_serial_str; + break; + } + } else + kdb_serial_ptr = kdb_serial_str; + } +#endif /* CONFIG_KDB */ flag = TTY_NORMAL; up->port.icount.rx++; @@ -2776,7 +2805,7 @@ serial8250_console_write(struct console if (up->port.sysrq) { /* serial8250_handle_port() already took the lock */ locked = 0; - } else if (oops_in_progress) { + } else if (oops_in_progress || KDB_8250()) { locked = spin_trylock(&up->port.lock); } else spin_lock(&up->port.lock); @@ -2834,6 +2863,30 @@ static int __init serial8250_console_set if (!port->iobase && !port->membase) return -ENODEV; +#ifdef CONFIG_KDB + /* + * Remember the line number of the first serial + * console. We'll make this the kdb serial console too. + */ + if (co && kdb_serial_line == -1) { + kdb_serial_line = co->index; + kdb_serial.io_type = port->iotype; + switch (port->iotype) { + case SERIAL_IO_MEM: +#ifdef SERIAL_IO_MEM32 + case SERIAL_IO_MEM32: +#endif + kdb_serial.iobase = (unsigned long)(port->membase); + kdb_serial.ioreg_shift = port->regshift; + break; + default: + kdb_serial.iobase = port->iobase; + kdb_serial.ioreg_shift = 0; + break; + } + } +#endif /* CONFIG_KDB */ + if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); --- a/drivers/serial/8250_early.c +++ b/drivers/serial/8250_early.c @@ -38,6 +38,11 @@ #include #endif +#ifdef CONFIG_KDB +#include +static int kdb_serial_line = -1; +#endif /* CONFIG_KDB */ + struct early_serial8250_device { struct uart_port port; char options[16]; /* e.g., 115200n8 */ @@ -231,6 +236,30 @@ int __init setup_early_serial8250_consol register_console(&early_serial8250_console); +#ifdef 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 = early_serial8250_console.index; + kdb_serial.io_type = early_device.port.iotype; + switch (early_device.port.iotype) { + case SERIAL_IO_MEM: +#ifdef SERIAL_IO_MEM32 + case SERIAL_IO_MEM32: +#endif + kdb_serial.iobase = (unsigned long)(early_device.port.membase); + kdb_serial.ioreg_shift = early_device.port.regshift; + break; + default: + kdb_serial.iobase = early_device.port.iobase; + kdb_serial.ioreg_shift = 0; + break; + } + } +#endif /* CONFIG_KDB */ + return 0; } --- a/drivers/serial/sn_console.c +++ b/drivers/serial/sn_console.c @@ -48,6 +48,22 @@ #include /* for mdelay */ #include #include +#ifdef CONFIG_KDB +#include +#include +#include +/* + * kdb_serial_line records the serial line number of the first serial console. + * NOTE: The kernel ignores characters on the serial line unless a user space + * program has opened the line first. To enter kdb before user space has opened + * the serial line, you can use the 'kdb=early' flag to lilo and set the + * appropriate breakpoints. + */ + +static int kdb_serial_line = -1; +static char *kdb_serial_ptr = (char *)kdb_serial_str; +#endif /* CONFIG_KDB */ + #include #include @@ -485,6 +501,26 @@ sn_receive_chars(struct sn_cons_port *po "obtaining data from the console (0x%0x)\n", ch); break; } +#ifdef CONFIG_KDB + if (kdb_on == 1) { + if (ch == *kdb_serial_ptr) { + if (!(*++kdb_serial_ptr)) { + spin_unlock_irqrestore(&port->sc_port.lock, flags); + if (!get_irq_regs()) { + KDB_STATE_SET(KEYBOARD); + KDB_ENTER(); /* to get some registers */ + } else + kdb(KDB_REASON_KEYBOARD, 0, get_irq_regs()); + kdb_serial_ptr = (char *)kdb_serial_str; + spin_lock_irqsave(&port->sc_port.lock, flags); + break; + } + } + else + kdb_serial_ptr = (char *)kdb_serial_str; + } +#endif /* CONFIG_KDB */ + #ifdef CONFIG_MAGIC_SYSRQ if (sysrq_requested) { unsigned long sysrq_timeout = sysrq_requested + HZ*5; @@ -1008,6 +1044,15 @@ sn_sal_console_write(struct console *co, */ static int sn_sal_console_setup(struct console *co, char *options) { +#ifdef 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; + } +#endif /* CONFIG_KDB */ return 0; } @@ -1083,3 +1128,31 @@ static int __init sn_sal_serial_console_ } console_initcall(sn_sal_serial_console_init); + +#ifdef CONFIG_KDB +int +l1_control_in_polled(int offset) +{ + int sal_call_status = 0, input; + int ret = 0; + if (offset == UART_LSR) { + ret = (UART_LSR_THRE | UART_LSR_TEMT); /* can send anytime */ + sal_call_status = ia64_sn_console_check(&input); + if (!sal_call_status && input) { + /* input pending */ + ret |= UART_LSR_DR; + } + } + return ret; +} + +int +l1_serial_in_polled(void) +{ + int ch; + if (!ia64_sn_console_getc(&ch)) + return ch; + else + return 0; +} +#endif /* CONFIG_KDB */ --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -40,6 +40,9 @@ #include #include #include +#ifdef CONFIG_KDB_USB +#include +#endif #include @@ -2271,6 +2274,74 @@ usb_hcd_platform_shutdown(struct platfor } EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown); +#ifdef CONFIG_KDB_USB +void * +usb_hcd_get_kdb_poll_func(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (hcd && hcd->driver) + return (void *)(hcd->driver->kdb_poll_char); + + return NULL; +} +EXPORT_SYMBOL_GPL (usb_hcd_get_kdb_poll_func); + +void * +usb_hcd_get_kdb_completion_func(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (hcd && hcd->driver) + return (void *)(hcd->driver->kdb_completion); + + return NULL; +} +EXPORT_SYMBOL_GPL (usb_hcd_get_kdb_completion_func); + +int +usb_hcd_check_uhci(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (hcd && hcd->driver){ + if (!(strcmp(hcd->driver->description, "uhci_hcd"))) + return 1; + } + + return 0; +} +EXPORT_SYMBOL_GPL (usb_hcd_check_uhci); + +kdb_hc_keyboard_attach_t +usb_hcd_get_hc_keyboard_attach(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (hcd && hcd->driver){ + return hcd->driver->kdb_hc_keyboard_attach; + } + + return 0; +} +EXPORT_SYMBOL_GPL (usb_hcd_get_hc_keyboard_attach); + +kdb_hc_keyboard_detach_t +usb_hcd_get_hc_keyboard_detach(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (hcd && hcd->driver){ + return hcd->driver->kdb_hc_keyboard_detach; + } + + return 0; +} +EXPORT_SYMBOL_GPL (usb_hcd_get_hc_keyboard_detach); + + +#endif /* CONFIG_KDB_USB */ + /*-------------------------------------------------------------------------*/ #if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -22,6 +22,9 @@ #ifdef __KERNEL__ #include +#ifdef CONFIG_KDB_USB +#include +#endif #define MAX_TOPO_LEVEL 6 @@ -287,6 +290,14 @@ struct hc_driver { int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags); int (*reset_device)(struct usb_hcd *, struct usb_device *); + +#ifdef CONFIG_KDB_USB + /* KDB poll function for this HC */ + int (*kdb_poll_char)(struct urb *urb); + void (*kdb_completion)(struct urb *urb); + kdb_hc_keyboard_attach_t kdb_hc_keyboard_attach; + kdb_hc_keyboard_detach_t kdb_hc_keyboard_detach; +#endif /* CONFIG_KDB_USB */ }; extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1092,6 +1092,48 @@ static int ehci_get_frame (struct usb_hc ehci->periodic_size; } +#ifdef CONFIG_KDB_USB + +int +ehci_kdb_poll_char(struct urb *urb) +{ + struct ehci_hcd *ehci; + + /* just to make sure */ + if (!urb || !urb->dev || !urb->dev->bus) + return -1; + + ehci = (struct ehci_hcd *) hcd_to_ehci(bus_to_hcd(urb->dev->bus)); + + /* make sure */ + if (!ehci) + return -1; + + if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) + return -1; + + /* + * If ehci->lock is held coming into this routine, it could + * mean KDB was entered while the HC driver was in the midst + * of processing URBs. Therefore it could be dangerous to + * processes URBs from this poll routine. And, we can't wait on + * the lock since we are in KDB and kernel threads (including the + * one holding the lock) are suspended. + * So, we punt and return an error. Keyboards attached to this + * HC will not be useable from KDB at this time. + */ + if (spin_is_locked(&ehci->lock)) + return -EBUSY; + + /* processes the URB */ + if (qh_completions_kdb(ehci, urb->hcpriv, urb)) + return 0; + + return -1; +} + +#endif /* CONFIG_KDB_USB */ + /*-------------------------------------------------------------------------*/ MODULE_DESCRIPTION(DRIVER_DESC); --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -22,6 +22,10 @@ #error "This file is PCI bus glue. CONFIG_PCI must be defined." #endif +#ifdef CONFIG_KDB_USB +#include +#endif + /*-------------------------------------------------------------------------*/ /* called after powerup, by probe or system-pm "wakeup" */ @@ -412,6 +416,10 @@ static const struct hc_driver ehci_pci_h .port_handed_over = ehci_port_handed_over, .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, + +#ifdef CONFIG_KDB_USB + .kdb_poll_char = ehci_kdb_poll_char, +#endif }; /*-------------------------------------------------------------------------*/ --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -577,6 +577,228 @@ halt: return count; } +#ifdef CONFIG_KDB_USB +/* + * This routine is basically a copy of qh_completions() for use by KDB. + * It is modified to only work on qtds which are associated + * with 'kdburb'. Also, there are some fixups related to locking. + */ +unsigned +qh_completions_kdb(struct ehci_hcd *ehci, struct ehci_qh *qh, struct urb *kdburb) +{ + struct ehci_qtd *last = NULL, *end = qh->dummy; + struct list_head *entry, *tmp; + int last_status = -EINPROGRESS; + int stopped; + unsigned count = 0; + int do_status = 0; + u8 state; + u32 halt = HALT_BIT(ehci); + + /* verify params are valid */ + if (!qh || !kdburb) + return 0; + + if (unlikely (list_empty (&qh->qtd_list))) + return count; + + /* completions (or tasks on other cpus) must never clobber HALT + * till we've gone through and cleaned everything up, even when + * they add urbs to this qh's queue or mark them for unlinking. + * + * NOTE: unlinking expects to be done in queue order. + */ + state = qh->qh_state; + qh->qh_state = QH_STATE_COMPLETING; + stopped = (state == QH_STATE_IDLE); + + /* remove de-activated QTDs from front of queue. + * after faults (including short reads), cleanup this urb + * then let the queue advance. + * if queue is stopped, handles unlinks. + */ + list_for_each_safe (entry, tmp, &qh->qtd_list) { + struct ehci_qtd *qtd; + struct urb *urb; + u32 token = 0; + int qtd_status; + + qtd = list_entry (entry, struct ehci_qtd, qtd_list); + urb = qtd->urb; + + if (urb != kdburb) + continue; + + /* clean up any state from previous QTD ...*/ + if (last) { + if (likely (last->urb != urb)) { + /* + * Lock hackery here... + * ehci_urb_done() makes the assumption + * that it's called with ehci->lock held. + * So, lock it if it isn't already. + */ + if (!spin_is_locked(&ehci->lock)) + spin_lock(&ehci->lock); + + ehci_urb_done(ehci, last->urb, last_status); + + /* + * ehci_urb_done() releases and reacquires + * ehci->lock, so release it here. + */ + if (spin_is_locked(&ehci->lock)) + spin_unlock (&ehci->lock); + + count++; + } + ehci_qtd_free (ehci, last); + last = NULL; + last_status = -EINPROGRESS; + } + + /* ignore urbs submitted during completions we reported */ + if (qtd == end) + break; + + /* hardware copies qtd out of qh overlay */ + rmb (); + token = hc32_to_cpu(ehci, qtd->hw_token); + + /* always clean up qtds the hc de-activated */ + if ((token & QTD_STS_ACTIVE) == 0) { + + if ((token & QTD_STS_HALT) != 0) { + stopped = 1; + + /* magic dummy for some short reads; qh won't advance. + * that silicon quirk can kick in with this dummy too. + */ + } else if (IS_SHORT_READ (token) + && !(qtd->hw_alt_next + & EHCI_LIST_END(ehci))) { + stopped = 1; + goto halt; + } + + /* stop scanning when we reach qtds the hc is using */ + } else if (likely (!stopped + && HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) { + break; + + } else { + stopped = 1; + + if (unlikely (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) + last_status = -ESHUTDOWN; + + /* ignore active urbs unless some previous qtd + * for the urb faulted (including short read) or + * its urb was canceled. we may patch qh or qtds. + */ + if (likely(last_status == -EINPROGRESS && + !urb->unlinked)) + continue; + + /* issue status after short control reads */ + if (unlikely (do_status != 0) + && QTD_PID (token) == 0 /* OUT */) { + do_status = 0; + continue; + } + + /* token in overlay may be most current */ + if (state == QH_STATE_IDLE + && cpu_to_hc32(ehci, qtd->qtd_dma) + == qh->hw_current) + token = hc32_to_cpu(ehci, qh->hw_token); + + /* force halt for unlinked or blocked qh, so we'll + * patch the qh later and so that completions can't + * activate it while we "know" it's stopped. + */ + if ((halt & qh->hw_token) == 0) { +halt: + qh->hw_token |= halt; + wmb (); + } + } + + /* remove it from the queue */ + qtd_status = qtd_copy_status(ehci, urb, qtd->length, token); + if (unlikely(qtd_status == -EREMOTEIO)) { + do_status = (!urb->unlinked && + usb_pipecontrol(urb->pipe)); + qtd_status = 0; + } + if (likely(last_status == -EINPROGRESS)) + last_status = qtd_status; + + if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { + last = list_entry (qtd->qtd_list.prev, + struct ehci_qtd, qtd_list); + last->hw_next = qtd->hw_next; + } + list_del (&qtd->qtd_list); + last = qtd; + } + + /* last urb's completion might still need calling */ + if (likely (last != NULL)) { + /* + * Lock hackery here... + * ehci_urb_done() makes the assumption + * that it's called with ehci->lock held. + * So, lock it if it isn't already. + */ + if (!spin_is_locked(&ehci->lock)) + spin_lock(&ehci->lock); + + ehci_urb_done(ehci, last->urb, last_status); + + /* + * ehci_urb_done() releases and reacquires + * ehci->lock, so release it here. + */ + if (spin_is_locked(&ehci->lock)) + spin_unlock (&ehci->lock); + + count++; + ehci_qtd_free (ehci, last); + } + + /* restore original state; caller must unlink or relink */ + qh->qh_state = state; + + /* be sure the hardware's done with the qh before refreshing + * it after fault cleanup, or recovering from silicon wrongly + * overlaying the dummy qtd (which reduces DMA chatter). + */ + if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END(ehci)) { + switch (state) { + case QH_STATE_IDLE: + qh_refresh(ehci, qh); + break; + case QH_STATE_LINKED: + /* should be rare for periodic transfers, + * except maybe high bandwidth ... + */ + if ((cpu_to_hc32(ehci, QH_SMASK) + & qh->hw_info2) != 0) { + intr_deschedule (ehci, qh); + (void) qh_schedule (ehci, qh); + } else + unlink_async (ehci, qh); + break; + /* otherwise, unlink already started */ + } + } + + return count; +} + +#endif /* CONFIG_KDB_USB */ + /*-------------------------------------------------------------------------*/ // high bandwidth multiplier, as encoded in highspeed endpoint descriptors --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -987,6 +987,73 @@ static int ohci_restart (struct ohci_hcd /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_KDB_USB + +int +ohci_kdb_poll_char(struct urb *urb) +{ + struct ohci_hcd *ohci; + struct ohci_regs * regs; + + /* just to make sure */ + if (!urb || !urb->dev || !urb->dev->bus) + return -1; + + ohci = (struct ohci_hcd *) hcd_to_ohci(bus_to_hcd(urb->dev->bus)); + + /* make sure */ + if (!ohci || !ohci->hcca) + return -1; + + if (!HC_IS_RUNNING (ohci_to_hcd(ohci)->state)) + return -1; + + /* + * If ohci->lock is held coming into this routine, it could + * mean KDB was entered while the HC driver was in the midst + * of processing URBs. Therefore it could be dangerous to + * processes URBs from this poll routine. And, we can't wait on + * the lock since we are in KDB and kernel threads (including the + * one holding the lock) are suspended. + * So, we punt and return an error. Keyboards attached to this + * HC will not be useable from KDB at this time. + */ + if (spin_is_locked(&ohci->lock)) + return -EBUSY; + + regs = ohci->regs; + + /* if the urb is not currently in progress resubmit it */ + if (urb->status != -EINPROGRESS) { + + if (usb_submit_urb (urb, GFP_ATOMIC)) + return -1; + + /* make sure the HC registers are set correctly */ + ohci_writel (ohci, OHCI_INTR_WDH, ®s->intrenable); + ohci_writel (ohci, OHCI_INTR_WDH, ®s->intrstatus); + ohci_writel (ohci, OHCI_INTR_MIE, ®s->intrenable); + + // flush those pci writes + (void) ohci_readl (ohci, &ohci->regs->control); + } + + if (ohci->hcca->done_head) { + dl_done_list_kdb (ohci, urb); + ohci_writel (ohci, OHCI_INTR_WDH, ®s->intrstatus); + // flush the pci write + (void) ohci_readl (ohci, &ohci->regs->control); + + return 0; + } + + return -1; +} + +#endif /* CONFIG_KDB_USB */ + +/*-------------------------------------------------------------------------*/ + MODULE_AUTHOR (DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE ("GPL"); --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -21,6 +21,10 @@ #include #include +#ifdef CONFIG_KDB_USB +#include +#endif + /* constants used to work around PM-related transfer * glitches in some AMD 700 series southbridges @@ -387,6 +391,7 @@ static int __devinit ohci_pci_start (str ohci_err (ohci, "can't start\n"); ohci_stop (hcd); } + return ret; } @@ -485,6 +490,9 @@ static const struct hc_driver ohci_pci_h .bus_resume = ohci_bus_resume, #endif .start_port_reset = ohci_start_port_reset, +#ifdef CONFIG_KDB_USB + .kdb_poll_char = ohci_kdb_poll_char, +#endif }; /*-------------------------------------------------------------------------*/ --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -1134,3 +1134,65 @@ dl_done_list (struct ohci_hcd *ohci) td = td_next; } } + + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_KDB_USB +static void +dl_done_list_kdb (struct ohci_hcd *ohci, struct urb *kdburb) +{ + struct td *td = dl_reverse_done_list (ohci); + + while (td) { + struct td *td_next = td->next_dl_td; + struct urb *urb = td->urb; + urb_priv_t *urb_priv = urb->hcpriv; + struct ed *ed = td->ed; + + if (urb != kdburb) { + td = td_next; + continue; + } + + /* update URB's length and status from TD */ + td_done (ohci, urb, td); + urb_priv->td_cnt++; + + /* If all this urb's TDs are done, just resubmit it */ + if (urb_priv->td_cnt == urb_priv->length) { + urb->actual_length = 0; + urb->status = -EINPROGRESS; + td_submit_urb (ohci, urb); + } + + /* clean schedule: unlink EDs that are no longer busy */ + if (list_empty (&ed->td_list)) { + if (ed->state == ED_OPER) + start_ed_unlink (ohci, ed); + + /* ... reenabling halted EDs only after fault cleanup */ + } else if ((ed->hwINFO & cpu_to_hc32 (ohci, ED_SKIP | ED_DEQUEUE)) + == cpu_to_hc32 (ohci, ED_SKIP)) { + td = list_entry (ed->td_list.next, struct td, td_list); + if (!(td->hwINFO & cpu_to_hc32 (ohci, TD_DONE))) { + ed->hwINFO &= ~cpu_to_hc32 (ohci, ED_SKIP); + /* ... hc may need waking-up */ + switch (ed->type) { + case PIPE_CONTROL: + ohci_writel (ohci, OHCI_CLF, + &ohci->regs->cmdstatus); + break; + case PIPE_BULK: + ohci_writel (ohci, OHCI_BLF, + &ohci->regs->cmdstatus); + break; + } + } + } + + td = td_next; + } +} + +#endif /* CONFIG_KDB_USB */ --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -50,6 +50,11 @@ #include "uhci-hcd.h" #include "pci-quirks.h" +#ifdef CONFIG_KDB_USB +#include +#include +#endif + /* * Version Information */ @@ -461,6 +466,213 @@ static irqreturn_t uhci_irq(struct usb_h return IRQ_HANDLED; } +#ifdef CONFIG_KDB_USB +/* Unlink KDB QH from hardware and software scheduler */ +static void kdb_unlink_uhci_qh(struct urb *urb, struct uhci_qh *qh) +{ + unsigned long flags; + struct uhci_hcd *uhci; + + uhci = (struct uhci_hcd *) hcd_to_uhci(bus_to_hcd(urb->dev->bus)); + + spin_lock_irqsave(&uhci->lock, flags); + unlink_interrupt(NULL, qh); + list_del(&(qh->node)); + spin_unlock_irqrestore(&uhci->lock, flags); + +} + +static int uhci_kdb_poll_char(struct urb *urb) +{ + if (!urb) /* can happen if no keyboard attached */ + return -1; + + return uhci_check_kdb_uhci_qh(kdb_uhci_keyboard_get_qh(urb)); +} + +/* Only 1 UHCI Keyboard supported */ +static inline void kdb_usb_fill_int_urb (struct urb *urb, + struct usb_device *dev, + unsigned int pipe, + void *transfer_buffer, + int buffer_length, + usb_complete_t complete_fn, + void *context, + int interval) +{ + urb->dev = dev; + urb->pipe = pipe; + urb->transfer_buffer = transfer_buffer; + urb->transfer_buffer_length = buffer_length; + urb->complete = complete_fn; + urb->context = context; + urb->interval = interval; + urb->start_frame = -1; +} + +static int kdb_uhci_keyboard_attach(int i, unsigned int usbhid_bufsize) +{ + struct urb *kdb_urb; + unsigned char *kdb_buffer; + dma_addr_t uhci_inbuf_dma; + struct urb *hid_inurb = kdb_usb_kbds[i].urb; + int ret = -1; + + kdb_usb_kbds[i].hid_urb = hid_inurb; + + kdb_urb = NULL; + kdb_buffer = NULL; + if (!(kdb_buffer = usb_buffer_alloc(hid_inurb->dev, + usbhid_bufsize, GFP_ATOMIC, + &uhci_inbuf_dma))) + goto out; + + if (!(kdb_urb = usb_alloc_urb(0, GFP_KERNEL))) + goto out; + + kdb_usb_fill_int_urb(kdb_urb, + hid_inurb->dev, + hid_inurb->pipe, + kdb_buffer, + hid_inurb->transfer_buffer_length, + hid_inurb->complete, + hid_inurb->context, + hid_inurb->interval + ); + + (kdb_urb)->transfer_dma = uhci_inbuf_dma; + (kdb_urb)->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + kdb_usb_kbds[i].urb = kdb_urb; + kdb_usb_kbds[i].buffer = kdb_buffer; + + if (usb_submit_urb(kdb_urb, GFP_ATOMIC)){ + kdb_usb_keyboard_detach(hid_inurb); + goto out; + } + /* Remove KDB special URB from endpoin queue to + * prevent hang during hid_disconnect(). + */ + list_del(&(kdb_urb->urb_list)); + + ret = 0; + return ret; +out: + /* Some Error Cleanup */ + ret = -1; + printk("KDB: Error, UHCI Keyboard HID won't work!\n"); + + if (kdb_buffer) + usb_buffer_free(hid_inurb->dev, + usbhid_bufsize, kdb_buffer, + uhci_inbuf_dma); + + if (kdb_urb) + usb_free_urb(kdb_urb); + + return ret; +} + +static int kdb_uhci_keyboard_detach(struct urb *urb, int i) +{ + int ret; + + if (kdb_usb_kbds[i].qh && (kdb_usb_kbds[i].hid_urb == urb)) { + /* UHCI keyboard */ + kdb_unlink_uhci_qh(kdb_usb_kbds[i].urb, kdb_usb_kbds[i].qh); + ret = 0; + } + ret = -1; + + return ret; +} + +/* Check if URB is managed by KDB code */ +static int kdb_uhci_keyboard_urb(struct urb *urb) +{ + int i; + + for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) { + if (kdb_usb_kbds[i].urb && kdb_usb_kbds[i].urb == urb) + return i; + } + return -1; +} + +/* Check if UHCI QH is managed by KDB code */ +static int kdb_uhci_keyboard_check_uhci_qh(struct uhci_qh *qh) +{ + int i; + + for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) { + if (kdb_usb_kbds[i].urb && kdb_usb_kbds[i].qh == qh) + return i; + } + return -1; +} + +/* Set UHCI QH using URB pointer */ +static int kdb_uhci_keyboard_set_qh(struct urb *urb, struct uhci_qh *qh) +{ + int i; + + i = kdb_uhci_keyboard_urb(urb); + if (i != -1) + kdb_usb_kbds[i].qh = qh; + + return 0; +} + +/* Get UHCI QH using URB pointer */ +static struct uhci_qh *kdb_uhci_keyboard_get_qh(struct urb *urb) +{ + int i; + + i = kdb_uhci_keyboard_urb(urb); + if (i != -1) + return kdb_usb_kbds[i].qh; + + return NULL; +} + +/* Set UHCI hid_event using URB pointer */ +static int kdb_uhci_keyboard_set_hid_event(struct urb *urb, int hid_event) +{ + int i; + + i = kdb_uhci_keyboard_urb(urb); + if (i != -1) + kdb_usb_kbds[i].kdb_hid_event = hid_event; + + return 0; +} +/* Get UHCI hid_event using URB pointer */ +static int kdb_uhci_keyboard_get_hid_event(struct urb *urb) +{ + int i; + + i = kdb_uhci_keyboard_urb(urb); + if (i != -1) + return kdb_usb_kbds[i].kdb_hid_event; + + return 0; +} + +/* Set UHCI hid_event using UHCI QH pointer */ +static int kdb_uhci_keyboard_set_hid_event_qh(struct uhci_qh *qh, int hid_event) +{ + int i; + + for (i = 0; i < KDB_USB_NUM_KEYBOARDS; i++) { + if (kdb_usb_kbds[i].urb && kdb_usb_kbds[i].qh == qh){ + kdb_usb_kbds[i].kdb_hid_event = hid_event; + return i; + } + } + return -1; +} +#endif + /* * Store the current frame number in uhci->frame_number if the controller * is runnning. Expand from 11 bits (of which we use only 10) to a @@ -935,6 +1147,12 @@ static const struct hc_driver uhci_drive .hub_status_data = uhci_hub_status_data, .hub_control = uhci_hub_control, +#ifdef CONFIG_KDB_USB + .kdb_poll_char = uhci_kdb_poll_char, + .kdb_completion = kdb_uhci_urb_complete, + .kdb_hc_keyboard_attach = kdb_uhci_keyboard_attach, + .kdb_hc_keyboard_detach = kdb_uhci_keyboard_detach, +#endif }; static const struct pci_device_id uhci_pci_ids[] = { { --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -25,6 +25,17 @@ * games with the FSBR code to make sure we get the correct order in all * the cases. I don't think it's worth the effort */ +#ifdef CONFIG_KDB_USB +/* KDB HID QH, managed by KDB code */ +static int kdb_uhci_keyboard_check_uhci_qh(struct uhci_qh *qh); +static int kdb_uhci_keyboard_set_qh(struct urb *urb, struct uhci_qh *qh); +static struct uhci_qh *kdb_uhci_keyboard_get_qh(struct urb *urb); +static int kdb_uhci_keyboard_set_hid_event(struct urb *urb, int hid_event); +static int kdb_uhci_keyboard_get_hid_event(struct urb *urb); +static int kdb_uhci_keyboard_set_hid_event_qh(struct uhci_qh *qh, int hid_event); +static int kdb_uhci_keyboard_urb(struct urb *urb); +#endif + static void uhci_set_next_interrupt(struct uhci_hcd *uhci) { if (uhci->is_stopped) @@ -288,6 +299,58 @@ static struct uhci_qh *uhci_alloc_qh(str return qh; } +#ifdef CONFIG_KDB_USB +/* + * Same as uhci_alloc_qh execpt it doesn't change to hep->hcpriv + */ +static struct uhci_qh *kdb_uhci_alloc_qh(struct uhci_hcd *uhci, + struct usb_device *udev, struct usb_host_endpoint *hep) +{ + dma_addr_t dma_handle; + struct uhci_qh *qh; + + qh = dma_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle); + if (!qh) + return NULL; + + memset(qh, 0, sizeof(*qh)); + qh->dma_handle = dma_handle; + + qh->element = UHCI_PTR_TERM; + qh->link = UHCI_PTR_TERM; + + INIT_LIST_HEAD(&qh->queue); + INIT_LIST_HEAD(&qh->node); + + if (udev) { /* Normal QH */ + qh->type = hep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (qh->type != USB_ENDPOINT_XFER_ISOC) { + qh->dummy_td = uhci_alloc_td(uhci); + if (!qh->dummy_td) { + dma_pool_free(uhci->qh_pool, qh, dma_handle); + return NULL; + } + } + qh->state = QH_STATE_IDLE; + qh->hep = hep; + qh->udev = udev; + + if (qh->type == USB_ENDPOINT_XFER_INT || + qh->type == USB_ENDPOINT_XFER_ISOC) + qh->load = usb_calc_bus_time(udev->speed, + usb_endpoint_dir_in(&hep->desc), + qh->type == USB_ENDPOINT_XFER_ISOC, + le16_to_cpu(hep->desc.wMaxPacketSize)) + / 1000 + 1; + + } else { /* Skeleton QH */ + qh->state = QH_STATE_ACTIVE; + qh->type = -1; + } + return qh; +} +#endif + static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) { WARN_ON(qh->state != QH_STATE_IDLE && qh->udev); @@ -1394,6 +1457,21 @@ static int uhci_urb_enqueue(struct usb_h if (!urbp) goto done; +#ifdef CONFIG_KDB_USB + /* Always allocate new QH for KDB URB. + * KDB HQ will be managed by KDB poll code not by + * UHCI HCD Driver. + */ + if (kdb_uhci_keyboard_urb(urb) != -1){ + /* KDB urb will be enqued only once */ + kdb_uhci_keyboard_set_qh(urb, NULL); + qh = kdb_uhci_alloc_qh(uhci, urb->dev, urb->ep); + if (!qh) + goto err_no_qh; + kdb_uhci_keyboard_set_qh(urb, qh); + } else +#endif + if (urb->ep->hcpriv) qh = urb->ep->hcpriv; else { @@ -1641,6 +1719,14 @@ static int uhci_advance_check(struct uhc int ret = 1; unsigned status; +#ifdef CONFIG_KDB_USB + /* Don't manage KDB QH */ + if(kdb_uhci_keyboard_check_uhci_qh(qh) != -1){ + ret = 0; + goto done; + } +#endif + if (qh->type == USB_ENDPOINT_XFER_ISOC) goto done; @@ -1733,6 +1819,11 @@ rescan: uhci->next_qh = list_entry(qh->node.next, struct uhci_qh, node); +#ifdef CONFIG_KDB_USB + /* Don't manage KDB QH */ + if(kdb_uhci_keyboard_check_uhci_qh(qh) != -1) + continue; +#endif if (uhci_advance_check(uhci, qh)) { uhci_scan_qh(uhci, qh); if (qh->state == QH_STATE_ACTIVE) { @@ -1759,3 +1850,76 @@ rescan: else uhci_set_next_interrupt(uhci); } + +#ifdef CONFIG_KDB_USB +/* + * Activate KDB UHCI QH, called by KDB poll code. + */ +static void kdb_activate_uhci_qh(struct uhci_qh *qh) +{ + struct urb_priv *urbp; + struct uhci_td *td; + __le32 status, token; + + urbp = list_entry(qh->queue.next, struct urb_priv, node); + + list_for_each_entry(td, &urbp->td_list, list){ + status = td->status; + token = td->token; + barrier(); + /* Clear Status and ActLen */ + status &= cpu_to_le32(0xff000000); + /* Make TD Active */ + status |= cpu_to_le32(TD_CTRL_ACTIVE); + /* Clear TD Interrupt */ + status &= cpu_to_le32(~TD_CTRL_IOC); + /* Toggle Data Sycronization Bit */ + if (token & cpu_to_le32(TD_TOKEN_TOGGLE)) + token &= cpu_to_le32(~TD_TOKEN_TOGGLE); + else + token |= cpu_to_le32(TD_TOKEN_TOGGLE); + + td->token = token; + td->status = status; + barrier(); + } + /* Activate KDB UHCI Keyboard HID QH */ + td = list_entry(urbp->td_list.next, struct uhci_td, list); + qh->element = LINK_TO_TD(td); + barrier(); +} + +/* + * Called when KDB finishes process key press/release event. + */ +static void +kdb_uhci_urb_complete (struct urb *urb) +{ + if (!kdb_uhci_keyboard_get_hid_event(urb)) + return; + + /* Activate KDB TD */ + kdb_activate_uhci_qh(kdb_uhci_keyboard_get_qh(urb)); + kdb_uhci_keyboard_set_hid_event(urb, 0); +} + +/* + * Check if state of KDB URB changed (key was pressed/released). + */ +static int uhci_check_kdb_uhci_qh(struct uhci_qh *qh) +{ + struct urb_priv *urbp = NULL; + struct uhci_td *td; + unsigned status; + + urbp = list_entry(qh->queue.next, struct urb_priv, node); + td = list_entry(urbp->td_list.next, struct uhci_td, list); + status = td_status(td); + if (!(status & TD_CTRL_ACTIVE)){ + /* We're okay, the queue has advanced */ + kdb_uhci_keyboard_set_hid_event_qh(qh, 1); + return 0; + } + return -1; +} +#endif --- a/fs/proc/meminfo.c +++ b/fs/proc/meminfo.c @@ -161,6 +161,151 @@ static int meminfo_proc_show(struct seq_ #undef K } +#ifdef CONFIG_KDB +#include +#include +/* Like meminfo_proc_show() but without the locks and using kdb_printf() */ +void +kdb_meminfo_proc_show(void) +{ + struct sysinfo i; + unsigned long committed; + unsigned long allowed; + struct vmalloc_info vmi; + long cached; + unsigned long pages[NR_LRU_LISTS]; + int lru; + +/* + * display in kilobytes. + */ +#define K(x) ((x) << (PAGE_SHIFT - 10)) + si_meminfo(&i); + kdb_si_swapinfo(&i); + committed = percpu_counter_read_positive(&vm_committed_as); + allowed = ((totalram_pages - hugetlb_total_pages()) + * sysctl_overcommit_ratio / 100) + total_swap_pages; + + cached = global_page_state(NR_FILE_PAGES) - + total_swapcache_pages - i.bufferram; + if (cached < 0) + cached = 0; + + get_vmalloc_info(&vmi); + + for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++) + pages[lru] = global_page_state(NR_LRU_BASE + lru); + + kdb_printf( + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" + "Buffers: %8lu kB\n", + K(i.totalram), + K(i.freeram), + K(i.bufferram) + ); + kdb_printf( + "Cached: %8lu kB\n" + "SwapCached: %8lu kB\n" + "Active: %8lu kB\n" + "Inactive: %8lu kB\n", + K(cached), + K(total_swapcache_pages), + K(pages[LRU_ACTIVE_ANON] + pages[LRU_ACTIVE_FILE]), + K(pages[LRU_INACTIVE_ANON] + pages[LRU_INACTIVE_FILE]) + ); + kdb_printf( + "Active(anon): %8lu kB\n" + "Inactive(anon): %8lu kB\n" + "Active(file): %8lu kB\n" + "Inactive(file): %8lu kB\n", + K(pages[LRU_ACTIVE_ANON]), + K(pages[LRU_INACTIVE_ANON]), + K(pages[LRU_ACTIVE_FILE]), + K(pages[LRU_INACTIVE_FILE]) + ); +#ifdef CONFIG_UNEVICTABLE_LRU + kdb_printf( + "Unevictable: %8lu kB\n" + "Mlocked: %8lu kB\n", + K(pages[LRU_UNEVICTABLE]), + K(global_page_state(NR_MLOCK)) + ); +#endif +#ifdef CONFIG_HIGHMEM + kdb_printf( + "HighTotal: %8lu kB\n" + "HighFree: %8lu kB\n" + "LowTotal: %8lu kB\n" + "LowFree: %8lu kB\n", + K(i.totalhigh), + K(i.freehigh), + K(i.totalram-i.totalhigh), + K(i.freeram-i.freehigh) + ); +#endif + kdb_printf( + "SwapTotal: %8lu kB\n" + "SwapFree: %8lu kB\n" + "Dirty: %8lu kB\n", + K(i.totalswap), + K(i.freeswap), + K(global_page_state(NR_FILE_DIRTY)) + ); + kdb_printf( + "Writeback: %8lu kB\n" + "AnonPages: %8lu kB\n" + "Mapped: %8lu kB\n", + K(global_page_state(NR_WRITEBACK)), + K(global_page_state(NR_ANON_PAGES)), + K(global_page_state(NR_FILE_MAPPED)) + ); + kdb_printf( + "Slab: %8lu kB\n" + "SReclaimable: %8lu kB\n" + "SUnreclaim: %8lu kB\n", + K(global_page_state(NR_SLAB_RECLAIMABLE) + + global_page_state(NR_SLAB_UNRECLAIMABLE)), + K(global_page_state(NR_SLAB_RECLAIMABLE)), + K(global_page_state(NR_SLAB_UNRECLAIMABLE)) + ); + kdb_printf( + "PageTables: %8lu kB\n" +#ifdef CONFIG_QUICKLIST + "Quicklists: %8lu kB\n" +#endif + "NFS_Unstable: %8lu kB\n" + "Bounce: %8lu kB\n", + K(global_page_state(NR_PAGETABLE)), +#ifdef CONFIG_QUICKLIST + K(quicklist_total_size()), +#endif + K(global_page_state(NR_UNSTABLE_NFS)), + K(global_page_state(NR_BOUNCE)) + ); + kdb_printf( + "WritebackTmp: %8lu kB\n" + "CommitLimit: %8lu kB\n" + "Committed_AS: %8lu kB\n", + K(global_page_state(NR_WRITEBACK_TEMP)), + K(allowed), + K(committed) + ); + kdb_printf( + "VmallocTotal: %8lu kB\n" + "VmallocUsed: %8lu kB\n" + "VmallocChunk: %8lu kB\n", + (unsigned long)VMALLOC_TOTAL >> 10, + vmi.used >> 10, + vmi.largest_chunk >> 10 + ); + +#ifdef CONFIG_HUGETLBFS + kdb_hugetlb_report_meminfo(); +#endif +} +#endif /* CONFIG_KDB */ + static int meminfo_proc_open(struct inode *inode, struct file *file) { return single_open(file, meminfo_proc_show, NULL); --- a/fs/proc/mmu.c +++ b/fs/proc/mmu.c @@ -14,11 +14,21 @@ #include #include "internal.h" +#ifdef CONFIG_KDB +#include +#endif + void get_vmalloc_info(struct vmalloc_info *vmi) { struct vm_struct *vma; unsigned long free_area_size; unsigned long prev_end; +#ifdef CONFIG_KDB + int get_lock = !KDB_IS_RUNNING(); +#else +#define get_lock 1 +#endif + vmi->used = 0; @@ -30,7 +40,8 @@ void get_vmalloc_info(struct vmalloc_inf prev_end = VMALLOC_START; - read_lock(&vmlist_lock); + if (get_lock) + read_lock(&vmlist_lock); for (vma = vmlist; vma; vma = vma->next) { unsigned long addr = (unsigned long) vma->addr; @@ -55,6 +66,7 @@ void get_vmalloc_info(struct vmalloc_inf if (VMALLOC_END - prev_end > vmi->largest_chunk) vmi->largest_chunk = VMALLOC_END - prev_end; - read_unlock(&vmlist_lock); + if (get_lock) + read_unlock(&vmlist_lock); } } --- a/include/asm-generic/kmap_types.h +++ b/include/asm-generic/kmap_types.h @@ -28,7 +28,8 @@ KMAP_D(15) KM_UML_USERCOPY, KMAP_D(16) KM_IRQ_PTE, KMAP_D(17) KM_NMI, KMAP_D(18) KM_NMI_PTE, -KMAP_D(19) KM_TYPE_NR +KMAP_D(19) KM_KDB, +KMAP_D(20) KM_TYPE_NR }; #undef KMAP_D --- a/include/linux/console.h +++ b/include/linux/console.h @@ -142,7 +142,12 @@ void vcs_remove_sysfs(int index); /* Some debug stub to catch some of the obvious races in the VT code */ #if 1 +#ifdef CONFIG_KDB +#include +#define WARN_CONSOLE_UNLOCKED() WARN_ON(!is_console_locked() && !oops_in_progress && !atomic_read(&kdb_event)) +#else /* !CONFIG_KDB */ #define WARN_CONSOLE_UNLOCKED() WARN_ON(!is_console_locked() && !oops_in_progress) +#endif /* CONFIG_KDB */ #else #define WARN_CONSOLE_UNLOCKED() #endif --- /dev/null +++ b/include/linux/dis-asm.h @@ -0,0 +1,347 @@ +/* Interface between the opcode library and its callers. + + Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + + 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, 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., 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. + + 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. */ + +/* Extracted from binutils 2.16.91.0.2 (OpenSUSE 10.0) and modified for kdb use. + * Any trailing whitespace was removed and #ifdef/ifndef __KERNEL__ added as + * required. + * Keith Owens 15 May 2006 + */ + +#ifndef DIS_ASM_H +#define DIS_ASM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __KERNEL__ +#include +#include +typedef void FILE; +#else /* __KERNEL__ */ +#include +#include "bfd.h" +#endif /* __KERNEL__ */ + +typedef int (*fprintf_ftype) (void *, const char*, ...) ATTRIBUTE_FPTR_PRINTF_2; + +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; + void *stream; + void *application_data; + + /* Target description. We could replace this with a pointer to the bfd, + but that would require one. There currently isn't any such requirement + so to avoid introducing one we record these explicitly. */ + /* The bfd_flavour. This can be bfd_target_unknown_flavour. */ + enum bfd_flavour flavour; + /* The bfd_arch value. */ + enum bfd_architecture arch; + /* The bfd_mach value. */ + unsigned long mach; + /* Endianness (for bi-endian cpus). Mono-endian cpus can ignore this. */ + enum bfd_endian endian; + /* An arch/mach-specific bitmask of selected instruction subsets, mainly + for processors with run-time-switchable instruction sets. The default, + zero, means that there is no constraint. CGEN-based opcodes ports + may use ISA_foo masks. */ + unsigned long insn_sets; + + /* Some targets need information about the current section to accurately + display insns. If this is NULL, the target disassembler function + will have to make its best guess. */ + asection *section; + + /* An array of pointers to symbols either at the location being disassembled + or at the start of the function being disassembled. The array is sorted + so that the first symbol is intended to be the one used. The others are + present for any misc. purposes. This is not set reliably, but if it is + not NULL, it is correct. */ + asymbol **symbols; + /* Number of symbols in array. */ + int num_symbols; + + /* For use by the disassembler. + The top 16 bits are reserved for public use (and are documented here). + The bottom 16 bits are for the internal use of the disassembler. */ + unsigned long flags; +#define INSN_HAS_RELOC 0x80000000 + void *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) + (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length, + struct disassemble_info *info); + + /* Function which should be called if we get an error that we can't + recover from. STATUS is the errno value from read_memory_func and + MEMADDR is the address that we were trying to read. INFO is a + pointer to this struct. */ + void (*memory_error_func) + (int status, bfd_vma memaddr, struct disassemble_info *info); + + /* Function called to print ADDR. */ + void (*print_address_func) + (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) + (bfd_vma addr, struct disassemble_info * info); + + /* Function called to check if a SYMBOL is can be displayed to the user. + This is used by some ports that want to hide special symbols when + displaying debugging outout. */ + bfd_boolean (* symbol_is_valid) + (asymbol *, struct disassemble_info * info); + + /* These are for buffer_read_memory. */ + bfd_byte *buffer; + bfd_vma buffer_vma; + unsigned int buffer_length; + + /* This variable may be set by the instruction decoder. It suggests + the number of bytes objdump should display on a single line. If + the instruction decoder sets this, it should always set it to + the same value in order to get reasonable looking output. */ + int bytes_per_line; + + /* The next two variables control the way objdump displays the raw data. */ + /* For example, if bytes_per_line is 8 and bytes_per_chunk is 4, the */ + /* output will look like this: + 00: 00000000 00000000 + with the chunks displayed according to "display_endian". */ + int bytes_per_chunk; + enum bfd_endian display_endian; + + /* Number of octets per incremented target address + Normally one, but some DSPs have byte sizes of 16 or 32 bits. */ + unsigned int octets_per_byte; + + /* The number of zeroes we want to see at the end of a section before we + start skipping them. */ + unsigned int skip_zeroes; + + /* The number of zeroes to skip at the end of a section. If the number + of zeroes at the end is between SKIP_ZEROES_AT_END and SKIP_ZEROES, + they will be disassembled. If there are fewer than + SKIP_ZEROES_AT_END, they will be skipped. This is a heuristic + attempt to avoid disassembling zeroes inserted by section + alignment. */ + unsigned int skip_zeroes_at_end; + + /* Results from instruction decoders. Not all decoders yet support + this information. This info is set each time an instruction is + decoded, and is only valid for the last such instruction. + + To determine whether this decoder supports this information, set + insn_info_valid to 0, decode an instruction, then check it. */ + + char insn_info_valid; /* Branch info has been set. */ + char branch_delay_insns; /* How many sequential insn's will run before + a branch takes effect. (0 = normal) */ + char data_size; /* Size of data reference in insn, in bytes */ + enum dis_insn_type insn_type; /* Type of instruction */ + bfd_vma target; /* Target address of branch or dref, if known; + zero if unknown. */ + bfd_vma target2; /* Second target address for dref2 */ + + /* Command line options specific to the target disassembler. */ + char * disassembler_options; + +} disassemble_info; + + +/* Standard disassemblers. Disassemble one instruction at the given + target address. Return number of octets processed. */ +typedef int (*disassembler_ftype) (bfd_vma, disassemble_info *); + +extern int print_insn_big_mips (bfd_vma, disassemble_info *); +extern int print_insn_little_mips (bfd_vma, disassemble_info *); +extern int print_insn_i386 (bfd_vma, disassemble_info *); +extern int print_insn_i386_att (bfd_vma, disassemble_info *); +extern int print_insn_i386_intel (bfd_vma, disassemble_info *); +extern int print_insn_ia64 (bfd_vma, disassemble_info *); +extern int print_insn_i370 (bfd_vma, disassemble_info *); +extern int print_insn_m68hc11 (bfd_vma, disassemble_info *); +extern int print_insn_m68hc12 (bfd_vma, disassemble_info *); +extern int print_insn_m68k (bfd_vma, disassemble_info *); +extern int print_insn_z8001 (bfd_vma, disassemble_info *); +extern int print_insn_z8002 (bfd_vma, disassemble_info *); +extern int print_insn_h8300 (bfd_vma, disassemble_info *); +extern int print_insn_h8300h (bfd_vma, disassemble_info *); +extern int print_insn_h8300s (bfd_vma, disassemble_info *); +extern int print_insn_h8500 (bfd_vma, disassemble_info *); +extern int print_insn_alpha (bfd_vma, disassemble_info *); +extern int print_insn_big_arm (bfd_vma, disassemble_info *); +extern int print_insn_little_arm (bfd_vma, disassemble_info *); +extern int print_insn_sparc (bfd_vma, disassemble_info *); +extern int print_insn_big_a29k (bfd_vma, disassemble_info *); +extern int print_insn_little_a29k (bfd_vma, disassemble_info *); +extern int print_insn_avr (bfd_vma, disassemble_info *); +extern int print_insn_d10v (bfd_vma, disassemble_info *); +extern int print_insn_d30v (bfd_vma, disassemble_info *); +extern int print_insn_dlx (bfd_vma, disassemble_info *); +extern int print_insn_fr30 (bfd_vma, disassemble_info *); +extern int print_insn_hppa (bfd_vma, disassemble_info *); +extern int print_insn_i860 (bfd_vma, disassemble_info *); +extern int print_insn_i960 (bfd_vma, disassemble_info *); +extern int print_insn_ip2k (bfd_vma, disassemble_info *); +extern int print_insn_m32r (bfd_vma, disassemble_info *); +extern int print_insn_m88k (bfd_vma, disassemble_info *); +extern int print_insn_maxq_little (bfd_vma, disassemble_info *); +extern int print_insn_maxq_big (bfd_vma, disassemble_info *); +extern int print_insn_mcore (bfd_vma, disassemble_info *); +extern int print_insn_mmix (bfd_vma, disassemble_info *); +extern int print_insn_mn10200 (bfd_vma, disassemble_info *); +extern int print_insn_mn10300 (bfd_vma, disassemble_info *); +extern int print_insn_ms1 (bfd_vma, disassemble_info *); +extern int print_insn_msp430 (bfd_vma, disassemble_info *); +extern int print_insn_ns32k (bfd_vma, disassemble_info *); +extern int print_insn_crx (bfd_vma, disassemble_info *); +extern int print_insn_openrisc (bfd_vma, disassemble_info *); +extern int print_insn_big_or32 (bfd_vma, disassemble_info *); +extern int print_insn_little_or32 (bfd_vma, disassemble_info *); +extern int print_insn_pdp11 (bfd_vma, disassemble_info *); +extern int print_insn_pj (bfd_vma, disassemble_info *); +extern int print_insn_big_powerpc (bfd_vma, disassemble_info *); +extern int print_insn_little_powerpc (bfd_vma, disassemble_info *); +extern int print_insn_rs6000 (bfd_vma, disassemble_info *); +extern int print_insn_s390 (bfd_vma, disassemble_info *); +extern int print_insn_sh (bfd_vma, disassemble_info *); +extern int print_insn_tic30 (bfd_vma, disassemble_info *); +extern int print_insn_tic4x (bfd_vma, disassemble_info *); +extern int print_insn_tic54x (bfd_vma, disassemble_info *); +extern int print_insn_tic80 (bfd_vma, disassemble_info *); +extern int print_insn_v850 (bfd_vma, disassemble_info *); +extern int print_insn_vax (bfd_vma, disassemble_info *); +extern int print_insn_w65 (bfd_vma, disassemble_info *); +extern int print_insn_xstormy16 (bfd_vma, disassemble_info *); +extern int print_insn_xtensa (bfd_vma, disassemble_info *); +extern int print_insn_sh64 (bfd_vma, disassemble_info *); +extern int print_insn_sh64x_media (bfd_vma, disassemble_info *); +extern int print_insn_frv (bfd_vma, disassemble_info *); +extern int print_insn_iq2000 (bfd_vma, disassemble_info *); +extern int print_insn_m32c (bfd_vma, disassemble_info *); + +extern disassembler_ftype arc_get_disassembler (void *); +extern disassembler_ftype cris_get_disassembler (bfd *); + +extern void print_mips_disassembler_options (FILE *); +extern void print_ppc_disassembler_options (FILE *); +extern void print_arm_disassembler_options (FILE *); +extern void parse_arm_disassembler_option (char *); +extern int get_arm_regname_num_options (void); +extern int set_arm_regname_option (int); +extern int get_arm_regnames (int, const char **, const char **, const char *const **); +extern bfd_boolean arm_symbol_is_valid (asymbol *, struct disassemble_info *); + +/* Fetch the disassembler for a given BFD, if that support is available. */ +extern disassembler_ftype disassembler (bfd *); + +/* Amend the disassemble_info structure as necessary for the target architecture. + Should only be called after initialising the info->arch field. */ +extern void disassemble_init_for_target (struct disassemble_info * info); + +/* Document any target specific options available from the disassembler. */ +extern void disassembler_usage (FILE *); + + +/* This block of definitions is for particular callers who read instructions + into a buffer before calling the instruction decoder. */ + +/* Here is a function which callers may wish to use for read_memory_func. + It gets bytes from a buffer. */ +extern int buffer_read_memory + (bfd_vma, bfd_byte *, unsigned int, struct disassemble_info *); + +/* This function goes with buffer_read_memory. + It prints a message using info->fprintf_func and info->stream. */ +extern void perror_memory (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 + (bfd_vma, struct disassemble_info *); + +/* Always true. */ +extern int generic_symbol_at_address + (bfd_vma, struct disassemble_info *); + +/* Also always true. */ +extern bfd_boolean generic_symbol_is_valid + (asymbol *, struct disassemble_info *); + +/* Method to initialize a disassemble_info struct. This should be + called by all applications creating such a struct. */ +extern void init_disassemble_info (struct disassemble_info *info, void *stream, + fprintf_ftype fprintf_func); + +/* For compatibility with existing code. */ +#define INIT_DISASSEMBLE_INFO(INFO, STREAM, FPRINTF_FUNC) \ + init_disassemble_info (&(INFO), (STREAM), (fprintf_ftype) (FPRINTF_FUNC)) +#define INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) \ + init_disassemble_info (&(INFO), (STREAM), (fprintf_ftype) (FPRINTF_FUNC)) + + +#ifdef __cplusplus +} +#endif + +#endif /* ! defined (DIS_ASM_H) */ --- /dev/null +++ b/include/linux/kdb.h @@ -0,0 +1,184 @@ +#ifndef _KDB_H +#define _KDB_H + +/* + * Kernel Debugger Architecture Independent Global Headers + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 2000-2007 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (C) 2000 Stephane Eranian + */ + +#include +#include +#include + +#ifdef CONFIG_KDB +/* These are really private, but they must be defined before including + * asm-$(ARCH)/kdb.h, so make them public and put them here. + */ +extern int kdb_getuserarea_size(void *, unsigned long, size_t); +extern int kdb_putuserarea_size(unsigned long, void *, size_t); + +#include +#endif + +#define KDB_MAJOR_VERSION 4 +#define KDB_MINOR_VERSION 4 +#define KDB_TEST_VERSION "" + +/* + * kdb_initial_cpu is initialized to -1, and is set to the cpu + * number whenever the kernel debugger is entered. + */ +extern volatile int kdb_initial_cpu; +extern atomic_t kdb_event; +extern atomic_t kdb_8250; +#ifdef CONFIG_KDB +#define KDB_IS_RUNNING() (kdb_initial_cpu != -1) +#define KDB_8250() (atomic_read(&kdb_8250) != 0) +#else +#define KDB_IS_RUNNING() (0) +#define KDB_8250() (0) +#endif /* CONFIG_KDB */ + +/* + * kdb_on + * + * Defines whether kdb is on or not. Default value + * is set by CONFIG_KDB_OFF. Boot with kdb=on/off/on-nokey + * or echo "[012]" > /proc/sys/kernel/kdb to change it. + */ +extern int kdb_on; + +#if defined(CONFIG_SERIAL_8250_CONSOLE) || defined(CONFIG_SERIAL_SGI_L1_CONSOLE) +/* + * kdb_serial.iobase is initialized to zero, and is set to the I/O + * address of the serial port when the console is setup in + * serial_console_setup. + */ +extern struct kdb_serial { + int io_type; + unsigned long iobase; + unsigned long ioreg_shift; +} kdb_serial; +#endif + +/* + * kdb_diemsg + * + * Contains a pointer to the last string supplied to the + * kernel 'die' panic function. + */ +extern const char *kdb_diemsg; + +#define KDB_FLAG_EARLYKDB (1 << 0) /* set from boot parameter kdb=early */ +#define KDB_FLAG_CATASTROPHIC (1 << 1) /* A catastrophic event has occurred */ +#define KDB_FLAG_CMD_INTERRUPT (1 << 2) /* Previous command was interrupted */ +#define KDB_FLAG_NOIPI (1 << 3) /* Do not send IPIs */ +#define KDB_FLAG_ONLY_DO_DUMP (1 << 4) /* Only do a dump, used when kdb is off */ +#define KDB_FLAG_NO_CONSOLE (1 << 5) /* No console is available, kdb is disabled */ +#define KDB_FLAG_NO_VT_CONSOLE (1 << 6) /* No VT console is available, do not use keyboard */ +#define KDB_FLAG_NO_I8042 (1 << 7) /* No i8042 chip is available, do not use keyboard */ +#define KDB_FLAG_RECOVERY (1 << 8) /* kdb is being entered for an error which has been recovered */ + +extern volatile int kdb_flags; /* Global flags, see kdb_state for per cpu state */ + +extern void kdb_save_flags(void); +extern void kdb_restore_flags(void); + +#define KDB_FLAG(flag) (kdb_flags & KDB_FLAG_##flag) +#define KDB_FLAG_SET(flag) ((void)(kdb_flags |= KDB_FLAG_##flag)) +#define KDB_FLAG_CLEAR(flag) ((void)(kdb_flags &= ~KDB_FLAG_##flag)) + +/* + * External entry point for the kernel debugger. The pt_regs + * at the time of entry are supplied along with the reason for + * entry to the kernel debugger. + */ + +typedef enum { + KDB_REASON_ENTER=1, /* KDB_ENTER() trap/fault - regs valid */ + KDB_REASON_ENTER_SLAVE, /* KDB_ENTER_SLAVE() trap/fault - regs valid */ + KDB_REASON_BREAK, /* Breakpoint inst. - regs valid */ + KDB_REASON_DEBUG, /* Debug Fault - regs valid */ + KDB_REASON_OOPS, /* Kernel Oops - regs valid */ + KDB_REASON_SWITCH, /* CPU switch - regs valid*/ + KDB_REASON_KEYBOARD, /* Keyboard entry - regs valid */ + KDB_REASON_NMI, /* Non-maskable interrupt; regs valid */ + KDB_REASON_RECURSE, /* Recursive entry to kdb; regs probably valid */ + KDB_REASON_CPU_UP, /* Add one cpu to kdb; regs invalid */ + KDB_REASON_SILENT, /* Silent entry/exit to kdb; regs invalid - internal only */ +} kdb_reason_t; + +#ifdef CONFIG_KDB +extern int kdb(kdb_reason_t, int, struct pt_regs *); +#else +#define kdb(reason,error_code,frame) (0) +#endif + +/* Mainly used by kdb code, but this function is sometimes used + * by hacked debug code so make it generally available, not private. + */ +extern void kdb_printf(const char *,...) + __attribute__ ((format (printf, 1, 2))); +typedef void (*kdb_printf_t)(const char *, ...) + __attribute__ ((format (printf, 1, 2))); +extern void kdb_init(void); + +#if defined(CONFIG_SMP) +/* + * Kernel debugger non-maskable IPI handler. + */ +extern int kdb_ipi(struct pt_regs *, void (*ack_interrupt)(void)); +extern void smp_kdb_stop(void); +#else /* CONFIG_SMP */ +#define smp_kdb_stop() +#endif /* CONFIG_SMP */ + +#ifdef CONFIG_KDB_USB + +#include + +typedef int (*kdb_hc_keyboard_attach_t)(int i, unsigned int bufsize); +typedef int (*kdb_hc_keyboard_detach_t)(struct urb *urb, int i); + +extern int kdb_usb_keyboard_attach(struct urb *urb, unsigned char *buffer, + void *poll_func, void *compl_func, + kdb_hc_keyboard_attach_t kdb_hc_keyboard_attach, + kdb_hc_keyboard_detach_t kdb_hc_keyboard_detach, + unsigned int bufsize, + struct urb *hid_urb); + +extern int kdb_usb_keyboard_detach(struct urb *urb); + +#endif /* CONFIG_KDB_USB */ + +static inline +int kdb_process_cpu(const struct task_struct *p) +{ + unsigned int cpu = task_thread_info(p)->cpu; + if (cpu > NR_CPUS) + cpu = 0; + return cpu; +} + +extern const char kdb_serial_str[]; + +#ifdef CONFIG_KDB_KDUMP +/* Define values for kdb_kdump_state */ +extern int kdb_kdump_state; /* KDB kdump state */ +#define KDB_KDUMP_RESET 0 +#define KDB_KDUMP_KDUMP 1 + +void kdba_kdump_prepare(struct pt_regs *); +void machine_crash_shutdown(struct pt_regs *); +void machine_crash_shutdown_begin(void); +void machine_crash_shutdown_end(struct pt_regs *); + +#endif /* CONFIG_KDB_KDUMP */ + +#endif /* !_KDB_H */ --- /dev/null +++ b/include/linux/kdbprivate.h @@ -0,0 +1,518 @@ +#ifndef _KDBPRIVATE_H +#define _KDBPRIVATE_H + +/* + * Kernel Debugger Architecture Independent Private Headers + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + */ + + +#include +#include +#include + + /* + * Kernel Debugger Error codes. Must not overlap with command codes. + */ + +#define KDB_NOTFOUND (-1) +#define KDB_ARGCOUNT (-2) +#define KDB_BADWIDTH (-3) +#define KDB_BADRADIX (-4) +#define KDB_NOTENV (-5) +#define KDB_NOENVVALUE (-6) +#define KDB_NOTIMP (-7) +#define KDB_ENVFULL (-8) +#define KDB_ENVBUFFULL (-9 ) +#define KDB_TOOMANYBPT (-10) +#define KDB_TOOMANYDBREGS (-11) +#define KDB_DUPBPT (-12) +#define KDB_BPTNOTFOUND (-13) +#define KDB_BADMODE (-14) +#define KDB_BADINT (-15) +#define KDB_INVADDRFMT (-16) +#define KDB_BADREG (-17) +#define KDB_BADCPUNUM (-18) +#define KDB_BADLENGTH (-19) +#define KDB_NOBP (-20) +#define KDB_BADADDR (-21) + + /* + * Kernel Debugger Command codes. Must not overlap with error codes. + */ +#define KDB_CMD_GO (-1001) +#define KDB_CMD_CPU (-1002) +#define KDB_CMD_SS (-1003) +#define KDB_CMD_SSB (-1004) + + /* + * Internal debug flags + */ +/* KDB_DEBUG_FLAG_BT 0x0001 Was Stack traceback debug */ +#define KDB_DEBUG_FLAG_BP 0x0002 /* Breakpoint subsystem debug */ +#define KDB_DEBUG_FLAG_BB_SUMM 0x0004 /* Basic block analysis, summary only */ +#define KDB_DEBUG_FLAG_AR 0x0008 /* Activation record, generic */ +#define KDB_DEBUG_FLAG_ARA 0x0010 /* Activation record, arch specific */ +#define KDB_DEBUG_FLAG_BB 0x0020 /* All basic block analysis */ +#define KDB_DEBUG_FLAG_STATE 0x0040 /* State flags */ +#define KDB_DEBUG_FLAG_MASK 0xffff /* All debug flags */ +#define KDB_DEBUG_FLAG_SHIFT 16 /* Shift factor for dbflags */ + +#define KDB_DEBUG(flag) (kdb_flags & (KDB_DEBUG_FLAG_##flag << KDB_DEBUG_FLAG_SHIFT)) +#define KDB_DEBUG_STATE(text,value) if (KDB_DEBUG(STATE)) kdb_print_state(text, value) + +typedef enum { + KDB_REPEAT_NONE = 0, /* Do not repeat this command */ + KDB_REPEAT_NO_ARGS, /* Repeat the command without arguments */ + KDB_REPEAT_WITH_ARGS, /* Repeat the command including its arguments */ +} kdb_repeat_t; + +typedef int (*kdb_func_t)(int, const char **); + + /* + * Symbol table format returned by kallsyms. + */ + +typedef struct __ksymtab { + unsigned long value; /* Address of symbol */ + const char *mod_name; /* Module containing symbol or "kernel" */ + unsigned long mod_start; + unsigned long mod_end; + const char *sec_name; /* Section containing symbol */ + unsigned long sec_start; + unsigned long sec_end; + const char *sym_name; /* Full symbol name, including any version */ + unsigned long sym_start; + unsigned long sym_end; + } kdb_symtab_t; +extern int kallsyms_symbol_next(char *prefix_name, int flag); +extern int kallsyms_symbol_complete(char *prefix_name, int max_len); + + /* + * Exported Symbols for kernel loadable modules to use. + */ +extern int kdb_register(char *, kdb_func_t, char *, char *, short); +extern int kdb_register_repeat(char *, kdb_func_t, char *, char *, short, kdb_repeat_t); +extern int kdb_unregister(char *); + +extern int kdb_getarea_size(void *, unsigned long, size_t); +extern int kdb_putarea_size(unsigned long, void *, size_t); + +/* Like get_user and put_user, kdb_getarea and kdb_putarea take variable + * names, not pointers. The underlying *_size functions take pointers. + */ +#define kdb_getarea(x,addr) kdb_getarea_size(&(x), addr, sizeof((x))) +#define kdb_putarea(addr,x) kdb_putarea_size(addr, &(x), sizeof((x))) + +extern int kdb_getphysword(unsigned long *word, + unsigned long addr, size_t size); +extern int kdb_getword(unsigned long *, unsigned long, size_t); +extern int kdb_putword(unsigned long, unsigned long, size_t); + +extern int kdbgetularg(const char *, unsigned long *); +extern char *kdbgetenv(const char *); +extern int kdbgetintenv(const char *, int *); +extern int kdbgetaddrarg(int, const char**, int*, unsigned long *, + long *, char **); +extern int kdbgetsymval(const char *, kdb_symtab_t *); +extern int kdbnearsym(unsigned long, kdb_symtab_t *); +extern void kdbnearsym_cleanup(void); +extern char *kdb_read(char *buffer, size_t bufsize); +extern char *kdb_strdup(const char *str, gfp_t type); +extern void kdb_symbol_print(kdb_machreg_t, const kdb_symtab_t *, unsigned int); + + /* + * Do we have a set of registers? + */ + +#define KDB_NULL_REGS(regs) \ + (regs == (struct pt_regs *)NULL ? kdb_printf("%s: null regs - should never happen\n", __FUNCTION__), 1 : 0) + + /* + * Routine for debugging the debugger state. + */ + +extern void kdb_print_state(const char *, int); + + /* + * Per cpu kdb state. A cpu can be under kdb control but outside kdb, + * for example when doing single step. + */ +volatile extern int kdb_state[ /*NR_CPUS*/ ]; +#define KDB_STATE_KDB 0x00000001 /* Cpu is inside kdb */ +#define KDB_STATE_LEAVING 0x00000002 /* Cpu is leaving kdb */ +#define KDB_STATE_CMD 0x00000004 /* Running a kdb command */ +#define KDB_STATE_KDB_CONTROL 0x00000008 /* This cpu is under kdb control */ +#define KDB_STATE_HOLD_CPU 0x00000010 /* Hold this cpu inside kdb */ +#define KDB_STATE_DOING_SS 0x00000020 /* Doing ss command */ +#define KDB_STATE_DOING_SSB 0x00000040 /* Doing ssb command, DOING_SS is also set */ +#define KDB_STATE_SSBPT 0x00000080 /* Install breakpoint after one ss, independent of DOING_SS */ +#define KDB_STATE_REENTRY 0x00000100 /* Valid re-entry into kdb */ +#define KDB_STATE_SUPPRESS 0x00000200 /* Suppress error messages */ +#define KDB_STATE_LONGJMP 0x00000400 /* longjmp() data is available */ +#define KDB_STATE_GO_SWITCH 0x00000800 /* go is switching back to initial cpu */ +#define KDB_STATE_PRINTF_LOCK 0x00001000 /* Holds kdb_printf lock */ +#define KDB_STATE_WAIT_IPI 0x00002000 /* Waiting for kdb_ipi() NMI */ +#define KDB_STATE_RECURSE 0x00004000 /* Recursive entry to kdb */ +#define KDB_STATE_IP_ADJUSTED 0x00008000 /* Restart IP has been adjusted */ +#define KDB_STATE_GO1 0x00010000 /* go only releases one cpu */ +#define KDB_STATE_KEYBOARD 0x00020000 /* kdb entered via keyboard on this cpu */ +#define KDB_STATE_KEXEC 0x00040000 /* kexec issued */ +#define KDB_STATE_ARCH 0xff000000 /* Reserved for arch specific use */ + +#define KDB_STATE_CPU(flag,cpu) (kdb_state[cpu] & KDB_STATE_##flag) +#define KDB_STATE_SET_CPU(flag,cpu) ((void)(kdb_state[cpu] |= KDB_STATE_##flag)) +#define KDB_STATE_CLEAR_CPU(flag,cpu) ((void)(kdb_state[cpu] &= ~KDB_STATE_##flag)) + +#define KDB_STATE(flag) KDB_STATE_CPU(flag,smp_processor_id()) +#define KDB_STATE_SET(flag) KDB_STATE_SET_CPU(flag,smp_processor_id()) +#define KDB_STATE_CLEAR(flag) KDB_STATE_CLEAR_CPU(flag,smp_processor_id()) + + /* + * kdb_nextline + * + * Contains the current line number on the screen. Used + * to handle the built-in pager (LINES env variable) + */ +extern volatile int kdb_nextline; + + /* + * Breakpoint state + * + * Each active and inactive breakpoint is represented by + * an instance of the following data structure. + */ + +typedef struct _kdb_bp { + bfd_vma bp_addr; /* Address breakpoint is present at */ + kdb_machinst_t bp_inst; /* Replaced instruction */ + + unsigned int bp_free:1; /* This entry is available */ + + unsigned int bp_enabled:1; /* Breakpoint is active in register */ + unsigned int bp_global:1; /* Global to all processors */ + + unsigned int bp_hardtype:1; /* Uses hardware register */ + unsigned int bp_forcehw:1; /* Force hardware register */ + unsigned int bp_installed:1; /* Breakpoint is installed */ + unsigned int bp_delay:1; /* Do delayed bp handling */ + unsigned int bp_delayed:1; /* Delayed breakpoint */ + + int bp_cpu; /* Cpu # (if bp_global == 0) */ + kdbhard_bp_t bp_template; /* Hardware breakpoint template */ + kdbhard_bp_t *bp_hard[NR_CPUS]; /* Hardware breakpoint structure */ + int bp_adjust; /* Adjustment to PC for real instruction */ +} kdb_bp_t; + + /* + * Breakpoint handling subsystem global variables + */ +extern kdb_bp_t kdb_breakpoints[/* KDB_MAXBPT */]; + + /* + * Breakpoint architecture dependent functions. Must be provided + * in some form for all architectures. + */ +extern void kdba_initbp(void); +extern void kdba_printbp(kdb_bp_t *); +extern void kdba_alloc_hwbp(kdb_bp_t *bp, int *diagp); +extern void kdba_free_hwbp(kdb_bp_t *bp); +extern int kdba_parsebp(int, const char**, int *, kdb_bp_t*); +extern char *kdba_bptype(kdbhard_bp_t *); +extern void kdba_setsinglestep(struct pt_regs *); +extern void kdba_clearsinglestep(struct pt_regs *); + + /* + * Adjust instruction pointer architecture dependent function. Must be + * provided in some form for all architectures. + */ +extern void kdba_adjust_ip(kdb_reason_t, int, struct pt_regs *); + + /* + * KDB-only global function prototypes. + */ +extern void kdb_id1(unsigned long); +extern void kdb_id_init(void); + + /* + * Initialization functions. + */ +extern void kdba_init(void); +extern void kdb_io_init(void); + + /* + * Architecture specific function to read a string. + */ +typedef int (*get_char_func)(void); +extern get_char_func poll_funcs[]; + +#ifndef CONFIG_IA64 + /* + * Data for a single activation record on stack. + */ + +struct kdb_stack_info { + kdb_machreg_t physical_start; + kdb_machreg_t physical_end; + kdb_machreg_t logical_start; + kdb_machreg_t logical_end; + kdb_machreg_t next; + const char * id; +}; + +typedef struct { DECLARE_BITMAP(bits, KDBA_MAXARGS); } valid_t; + +struct kdb_activation_record { + struct kdb_stack_info stack; /* information about current stack */ + int args; /* number of arguments detected */ + kdb_machreg_t arg[KDBA_MAXARGS]; /* -> arguments */ + valid_t valid; /* is argument n valid? */ +}; +#endif + + /* + * Architecture specific Stack Traceback functions. + */ + +struct task_struct; + +extern int kdba_bt_address(kdb_machreg_t, int); +extern int kdba_bt_process(const struct task_struct *, int); + + /* + * KDB Command Table + */ + +typedef struct _kdbtab { + char *cmd_name; /* Command name */ + kdb_func_t cmd_func; /* Function to execute command */ + char *cmd_usage; /* Usage String for this command */ + char *cmd_help; /* Help message for this command */ + short cmd_flags; /* Parsing flags */ + short cmd_minlen; /* Minimum legal # command chars required */ + kdb_repeat_t cmd_repeat; /* Does command auto repeat on enter? */ +} kdbtab_t; + + /* + * External command function declarations + */ + +extern int kdb_id(int, const char **); +extern int kdb_bt(int, const char **); + + /* + * External utility function declarations + */ +extern char* kdb_getstr(char *, size_t, char *); + + /* + * Register contents manipulation + */ +extern int kdba_getregcontents(const char *, struct pt_regs *, kdb_machreg_t *); +extern int kdba_setregcontents(const char *, struct pt_regs *, kdb_machreg_t); +extern int kdba_dumpregs(struct pt_regs *, const char *, const char *); +extern int kdba_setpc(struct pt_regs *, kdb_machreg_t); +extern kdb_machreg_t kdba_getpc(struct pt_regs *); + + /* + * Debug register handling. + */ +extern void kdba_installdbreg(kdb_bp_t*); +extern void kdba_removedbreg(kdb_bp_t*); + + /* + * Breakpoint handling - External interfaces + */ +extern void kdb_initbptab(void); +extern void kdb_bp_install_global(struct pt_regs *); +extern void kdb_bp_install_local(struct pt_regs *); +extern void kdb_bp_remove_global(void); +extern void kdb_bp_remove_local(void); + + /* + * Breakpoint handling - Internal to kdb_bp.c/kdba_bp.c + */ +extern int kdba_installbp(struct pt_regs *regs, kdb_bp_t *); +extern int kdba_removebp(kdb_bp_t *); + + +typedef enum { + KDB_DB_BPT, /* Breakpoint */ + KDB_DB_SS, /* Single-step trap */ + KDB_DB_SSB, /* Single step to branch */ + KDB_DB_SSBPT, /* Single step over breakpoint */ + KDB_DB_NOBPT /* Spurious breakpoint */ +} kdb_dbtrap_t; + +extern kdb_dbtrap_t kdba_db_trap(struct pt_regs *, int); /* DEBUG trap/fault handler */ +extern kdb_dbtrap_t kdba_bp_trap(struct pt_regs *, int); /* Breakpoint trap/fault hdlr */ + + /* + * Interrupt Handling + */ +typedef unsigned long kdb_intstate_t; + +extern void kdba_disableint(kdb_intstate_t *); +extern void kdba_restoreint(kdb_intstate_t *); + + /* + * SMP and process stack manipulation routines. + */ +extern int kdba_ipi(struct pt_regs *, void (*)(void)); +extern int kdba_main_loop(kdb_reason_t, kdb_reason_t, int, kdb_dbtrap_t, struct pt_regs *); +extern int kdb_main_loop(kdb_reason_t, kdb_reason_t, int, kdb_dbtrap_t, struct pt_regs *); + + /* + * General Disassembler interfaces + */ +extern int kdb_dis_fprintf(PTR, const char *, ...) __attribute__ ((format (printf, 2, 3))); +extern int kdb_dis_fprintf_dummy(PTR, const char *, ...) __attribute__ ((format (printf, 2, 3))); +extern disassemble_info kdb_di; + + /* + * Architecture Dependent Disassembler interfaces + */ +extern int kdba_id_printinsn(kdb_machreg_t, disassemble_info *); +extern int kdba_id_parsemode(const char *, disassemble_info*); +extern void kdba_id_init(disassemble_info *); +extern void kdba_check_pc(kdb_machreg_t *); + + /* + * Miscellaneous functions and data areas + */ +extern char *kdb_cmds[]; +extern void debugger_syslog_data(char *syslog_data[]); +extern unsigned long kdb_task_state_string(const char *); +extern char kdb_task_state_char (const struct task_struct *); +extern unsigned long kdb_task_state(const struct task_struct *p, unsigned long mask); +extern void kdb_ps_suppressed(void); +extern void kdb_ps1(const struct task_struct *p); +extern int kdb_parse(const char *cmdstr); +extern void kdb_print_nameval(const char *name, unsigned long val); +extern void kdb_send_sig_info(struct task_struct *p, struct siginfo *info, int seqno); +#ifdef CONFIG_SWAP +extern void kdb_si_swapinfo(struct sysinfo *); +#else +#include +#define kdb_si_swapinfo(x) si_swapinfo(x) +#endif +extern void kdb_meminfo_proc_show(void); +#ifdef CONFIG_HUGETLB_PAGE +extern void kdb_hugetlb_report_meminfo(void); +#endif /* CONFIG_HUGETLB_PAGE */ +extern const char *kdb_walk_kallsyms(loff_t *pos); + + /* + * Architecture Dependant Local Processor setup & cleanup interfaces + */ +extern void kdba_local_arch_setup(void); +extern void kdba_local_arch_cleanup(void); + + /* + * Defines for kdb_symbol_print. + */ +#define KDB_SP_SPACEB 0x0001 /* Space before string */ +#define KDB_SP_SPACEA 0x0002 /* Space after string */ +#define KDB_SP_PAREN 0x0004 /* Parenthesis around string */ +#define KDB_SP_VALUE 0x0008 /* Print the value of the address */ +#define KDB_SP_SYMSIZE 0x0010 /* Print the size of the symbol */ +#define KDB_SP_NEWLINE 0x0020 /* Newline after string */ +#define KDB_SP_DEFAULT (KDB_SP_VALUE|KDB_SP_PAREN) + +/* Save data about running processes */ + +struct kdb_running_process { + struct task_struct *p; + struct pt_regs *regs; + int seqno; /* kdb sequence number */ + int irq_depth; /* irq count */ + struct kdba_running_process arch; /* arch dependent save data */ +}; + +extern struct kdb_running_process kdb_running_process[/* NR_CPUS */]; + +extern int kdb_save_running(struct pt_regs *, kdb_reason_t, kdb_reason_t, int, kdb_dbtrap_t); +extern void kdb_unsave_running(struct pt_regs *); +extern struct task_struct *kdb_curr_task(int); + +/* Incremented each time the main kdb loop is entered on the initial cpu, + * it gives some indication of how old the saved data is. + */ +extern int kdb_seqno; + +#define kdb_task_has_cpu(p) (task_curr(p)) +extern void kdb_runqueue(unsigned long cpu, kdb_printf_t xxx_printf); + +/* Simplify coexistence with NPTL */ +#define kdb_do_each_thread(g, p) do_each_thread(g, p) +#define kdb_while_each_thread(g, p) while_each_thread(g, p) + +#define GFP_KDB (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) + +extern void *debug_kmalloc(size_t size, gfp_t flags); +extern void debug_kfree(void *); +extern void debug_kusage(void); + +extern void kdba_set_current_task(const struct task_struct *); +extern const struct task_struct *kdb_current_task; +extern struct pt_regs *kdb_current_regs; + +/* Functions to safely read and write kernel areas. The {to,from}_xxx + * addresses are not necessarily valid, these functions must check for + * validity. If the arch already supports get and put routines with suitable + * validation and/or recovery on invalid addresses then use those routines, + * otherwise check it yourself. + */ + +extern int kdba_putarea_size(unsigned long to_xxx, void *from, size_t size); +extern int kdba_getarea_size(void *to, unsigned long from_xxx, size_t size); +extern int kdba_verify_rw(unsigned long addr, size_t size); + +#ifndef KDB_RUNNING_PROCESS_ORIGINAL +#define KDB_RUNNING_PROCESS_ORIGINAL kdb_running_process +#endif + +extern int kdb_wait_for_cpus_secs; +extern void kdba_cpu_up(void); +extern char kdb_prompt_str[]; + +#define KDB_WORD_SIZE ((int)sizeof(kdb_machreg_t)) + +#ifdef CONFIG_KDB_USB +#include + +/* support up to 8 USB keyboards (probably excessive, but...) */ +#define KDB_USB_NUM_KEYBOARDS 8 + +struct kdb_usb_kbd_info { + struct urb *urb; /* pointer to the URB */ + unsigned char *buffer; /* pointer to the kbd char buffer */ + int (*poll_func)(struct urb *urb); /* poll function to retrieve chars */ + int poll_ret; /* return val from poll_func */ + int caps_lock; /* state of the caps lock for this keyboard */ + struct uhci_qh *qh; + int kdb_hid_event; + struct urb *hid_urb; /* pointer to the HID URB */ + /* USB Host Controller specific callbacks */ + kdb_hc_keyboard_attach_t kdb_hc_keyboard_attach; + kdb_hc_keyboard_detach_t kdb_hc_keyboard_detach; + int (*kdb_hc_urb_complete)(struct urb *urb); /* called when URB int is + processed */ + +}; + +extern struct kdb_usb_kbd_info kdb_usb_kbds[KDB_USB_NUM_KEYBOARDS]; + +#endif /* CONFIG_KDB_USB */ + +#ifdef CONFIG_KDB_KDUMP +#define KDUMP_REASON_RESET 0 +extern void kdba_kdump_shutdown_slave(struct pt_regs *); +#endif /* CONFIG_KDB_KDUMP */ + +#endif /* !_KDBPRIVATE_H */ --- a/include/linux/reboot.h +++ b/include/linux/reboot.h @@ -53,7 +53,14 @@ extern void machine_power_off(void); extern void machine_shutdown(void); struct pt_regs; +#ifdef CONFIG_KDB_KDUMP extern void machine_crash_shutdown(struct pt_regs *); +extern void machine_crash_shutdown_begin(void); +extern void machine_crash_shutdown_other_cpu(struct pt_regs *); +extern void machine_crash_shutdown_end(struct pt_regs *); +#else +extern void machine_crash_shutdown(struct pt_regs *); +#endif /* !CONFIG_KDB_KDUMP */ /* * Architecture independent implemenations of sys_reboot commands. --- a/init/main.c +++ b/init/main.c @@ -101,6 +101,10 @@ extern void tc_init(void); enum system_states system_state __read_mostly; EXPORT_SYMBOL(system_state); +#ifdef CONFIG_KDB +#include +#endif /* CONFIG_KDB */ + /* * Boot command-line arguments */ @@ -203,6 +207,26 @@ static const char *panic_later, *panic_p extern struct obs_kernel_param __setup_start[], __setup_end[]; +#ifdef CONFIG_KDB +static int __init kdb_setup(char *str) +{ + if (strcmp(str, "on") == 0) { + kdb_on = 1; + } else if (strcmp(str, "on-nokey") == 0) { + kdb_on = 2; + } else if (strcmp(str, "off") == 0) { + kdb_on = 0; + } else if (strcmp(str, "early") == 0) { + kdb_on = 1; + kdb_flags |= KDB_FLAG_EARLYKDB; + } else + printk("kdb flag %s not recognised\n", str); + return 0; +} + +__setup("kdb=", kdb_setup); +#endif /* CONFIG_KDB */ + static int __init obsolete_checksetup(char *line) { struct obs_kernel_param *p; @@ -664,6 +688,14 @@ asmlinkage void __init start_kernel(void calibrate_delay(); pidmap_init(); anon_vma_init(); + +#ifdef CONFIG_KDB + kdb_init(); + if (KDB_FLAG(EARLYKDB)) { + KDB_ENTER(); + } +#endif /* CONFIG_KDB */ + #ifdef CONFIG_X86 if (efi_enabled) efi_enter_virtual_mode(); --- /dev/null +++ b/kdb/ChangeLog @@ -0,0 +1,2040 @@ +2008-11-26 Jay Lan + + * kdb-v4.4-2.6.28-rc6-common-1. + +2008-11-12 Jay Lan + + * kdb-v4.4-2.6.28-rc4-common-1. + +2008-11-04 Jay Lan + + * medusa needs kdb to handle '\n' in kdb_read(), + Cliff Wickman + * kdb-v4.4-2.6.28-rc3-common-1. + +2008-10-29 Jay Lan + + * "Commandeer vector 0xfe for KDB_VECTOR", version 2. + Cliff Wickman + * kdb-v4.4-2.6.28-rc2-common-2. + +2008-10-27 Jay Lan + + * kdb-v4.4-2.6.28-rc2-common-1. + +2008-10-20 Jay Lan + + * kdb-v4.4-2.6.27-common-1. + +2008-09-30 Jay Lan + + * kdb-v4.4-2.6.27-rc8-common-1. + +2008-09-22 Jay Lan + + * kdb-v4.4-2.6.27-rc7-common-1. + +2008-09-03 Jay Lan + + * kdb-v4.4-2.6.27-rc5-common-1. + +2008-08-19 Jay Lan + + * kdb-v4.4-2.6.27-rc3-common-1. + +2008-08-15 Jay Lan + + * mm_online_pgdat_export_symbol, Jay Lan + - Fix compilation error by exporting first_online_pgdat & + next_online_pgdat for 'pgdat' command. + * kdb-v4.4-2.6.27-rc2-common-2.1. + +2008-08-14 Jay Lan + + * Support 'kdump' command to take a kdump vmcore from KDB, + Dan Aloni (da-x@monatomic.org), + Jason Xiao (jidong.xiao@gmail.com), + Jay Lan (jlan@sgi.com) + * kdb-v4.4-2.6.27-rc2-common-2. + +2008-08-06 Jay Lan + + * Fix up the NULL pointer deference issue in ohci_kdb_poll_char, + Jason Xiao + * kdb-v4.4-2.6.27-rc2-common-1. + +2008-07-18 Jay Lan + + * support Hardware Breakpoint (bph/bpha) commands + IA64: Greg Banks + X86: Konstantin Baydarov + * kdb-v4.4-2.6.26-common-2. + +2008-07-14 Jay Lan + + * kdb-v4.4-2.6.26-common-1. + +2008-07-11 Jay Lan + + * New commands and some fixups and enhancements, + Joe Korty + John Blackwood + Jim Houston + - Use the non-sleeping copy_from_user_atomic. + - Enhance kdb_cmderror diagnostic output. + - Expand the KDB 'duplicate command' error message. + - Touch NMI watchdog in various KDB busy-loops. + - Support IMB HS20 Blade 8843 platform. + - Display exactly which cpus needed an NMI to get them into kdb. + - Better document that kdb's 'ps A' command can be used to show + _all_ processes and threads + - Suppress KDB boottime INFO messages if quiet boot. + - Add a KDB breakpoint to the OOPs path. + - Add CONFIG_DISCONTIGMEM support to kdbm_memmap. + - Extend the KDB task command to handle CONFIG_NUMA fields. + - Extend the KDB vm command to support NUMA stuff. + - Create the KDB mempolicy command. + - Create a pgdat command for KDB. + - Fix a hang on boot on some i386 systems. + * kdb-v4.4-2.6.26-rc9-common-1. + +2008-06-30 Jay Lan + + * compilation warning cleanup, Cliff Wickman + * kdb-v4.4-2.6.26-rc8-common-1. + +2008-06-25 Jay Lan + + * Added John Blackwood to the authors of + kdb-v4.4-2.6.26-rc4-common-2. + * kdb-v4.4-2.6.26-rc7-common-1. + +2008-06-24 Jay Lan + + * support lcrash style debug_info file: Cliff Wickman + - It adds to kdb the ability to symbolically dereference structure + pointers through a lcrash-style debug_info file. + - Implements "print", "px", and "pd" print commands. + - Implements "walk" command to follow linked lists. + - Implements "whatis" to display a structure (with offsets). + - Implements "sizeof" for types (structures, typedefs, etc.). + * kdb-v4.4-2.6.26-rc5-common-2. + +2008-06-06 Jay Lan + + * kdb-v4.4-2.6.26-rc5-common-1. + +2008-06-05 Jay Lan + + * fixed 'rq/rqa' command runs off the end of runqueue's rt.active + priority bitmap array, John Blackwood & + Lachlan McIlroy + * kdb-v4.4-2.6.26-rc4-common-2. + +2008-05-30 Jay Lan + + * kdb-v4.4-2.6.26-rc4-common-1. + +2008-05-20 Jay Lan + + * kdb-v4.4-2.6.26-rc3-common-1. + +2008-05-13 Jay Lan + + * XPC support is removed from KDB due to XPC changes in 2.6.26-rc1. + * kdb-v4.4-2.6.26-rc1-common-1. + +2008-04-17 Jay Lan + + * kdb-v4.4-2.6.25-common-1. + +2008-03-16 Jay Lan + + * kdb-v4.4-2.6.25-rc6-common-1. + +2008-03-03 Jay Lan + + * kdb-v4.4-2.6.25-rc3-common-1. + +2008-02-26 Jay Lan + + * remove 'fastcall' from kdb code. + * kdb-v4.4-2.6.25-rc2-common-1. + +2008-02-19 Jay Lan + + * kdb-v4.4-2.6.25-rc1-common-1. + +2008-02-06 Jay Lan + + * Backed out USB UHCI support since it caused dropped characters and + broke OHCI. + * Restored "archkdbcommon" commands for x86. It was lost at the x86 + merge. + * Detecting if the HC was "busy", Aaron Young + * kdb-v4.4-2.6.24-common-2. + +2008-01-29 Jay Lan + + * kdb-v4.4-2.6.24-common-1. + +2008-01-22 Jay Lan + + * USB UHCI kdb support, Konstantin Baydarov + * kdb-v4.4-2.6.24-rc8-common-3. + +2008-01-18 Jay Lan + + * USB EHCI kdb support, Aaron Young + * kdb-v4.4-2.6.24-rc8-common-2. + +2008-01-18 Jay Lan + + * kdb-v4.4-2.6.24-rc8-common-1. + +2008-01-07 Jay Lan + + * kdb-v4.4-2.6.24-rc7-common-1. + +2007-12-21 Jay Lan + + * Renamed kdb/kdba_bt_x86.c to arch/x86/kdba_bt.c. And thus, the x86 + backtrace code is now moved into the kdb x86 patch. + * kdb v4.4-2.6.24-rc6-common-1. + +2007-12-12 Jay Lan + + * kdb v4.4-2.6.24-rc5-common-1. + +2007-12-05 Jay Lan + + * Fixed a 'sysctl table check failed' problem. + * kdb v4.4-2.6.24-rc4-common-1. + +2007-11-26 Jay Lan + + * kdb v4.4-2.6.24-rc3-common-1. + +2007-11-13 Jay Lan + + * Back ported "New KDB USB interface" from Aaron Young in + v4.4-2.6.23-common-2 to 2.6.24 kdb patchset. + * kdb v4.4-2.6.24-rc2-common-2. + +2007-11-12 Jay Lan + + * kdb v4.4-2.6.24-rc2-common-1. + +2007-11-09 Jay Lan + + * Rebase to 2.6.24-rc1 kernel + * - merged kdb-v4.4-2.6.23-i386-1 and kdb-v4.4-2.6.23-x86_64-1 + * into kdb-v4.4-2.6.24-rc1-x86-1 + * - Fields "done", "sglist_len", and "pid" are removed from + * struct scsi_cmnd. Thus, these fields are no longer displayed + * on "sc" command. + * kdb v4.4-2.6.24-rc1-common-1. + +2007-11-08 Jay Lan + + * New KDB USB interface, Aaron Young + * 1. This patch allows KDB to work with any Host Contoller driver + * and call the correct HC driver poll routine (as long as the + * HC driver provides a .kdb_poll_char routine via it's + * associated hc_driver struct). + * 2. Hotplugged keyboards are now recognized by KDB. + * 3. Currently KDB can only make use of 1 USB type keyboard. + * New code can handle up to 8 attached keyboards - input is + * multiplexed from all of them while in kdb. + * kdb v4.4-2.6.23-common-2. + +2007-10-24 Jay Lan + + * kdb v4.4-2.6.23-common-1. + +2007-09-26 Jay Lan + + * kdb v4.4-2.6.23-rc8-common-1. + +2007-09-21 Jay Lan + + * kdb v4.4-2.6.23-rc7-common-1. + +2007-09-12 Jay Lan + + * kdb v4.4-2.6.23-rc6-common-1. + +2007-09-06 Jay Lan + + * kdb v4.4-2.6.23-rc5-common-1. + +2007-08-30 Keith Owens + + * New i386/x86_64 backtrace requires that kdb_save_running() does not + exit until after kdb_main_loop() has completed. + * List more noret functions in i386/x86_64 backtrace code. + * Call to a noret function ends a basic block. + * After a call to a noret function, eip/rip may be pointing at the next + function or not, depending on function alignment. Jay Lan. + * kdb v4.4-2.6.23-rc4-common-2. + +2007-08-30 Jay Lan + + * kdb v4.4-2.6.23-rc4-common-1. + +2007-08-28 Keith Owens + + * kdb/kdba_bt_x86.c: + * Handle the variable amount of stack data that is pushed by x86_64 + * hardware on an interrupt. + * Add instruction vmsave. + * Handle pop to %rsp. + * Cope with return address for functions defined as ATTRIB_NORET. + * Include CONFIG_DEBUG_INFO in the summary line of bb_all. + * Check for an interrupt that was delivered while user space was in + * control. + * A return to child_rip ends a backtrace. + * Ignore level2_kernel_pgt and level3_kernel_pgt data areas if they + * occur within the text segment. + * kdb v4.4-2.6.23-rc3-common-2. + +2007-08-24 Keith Owens + + * kdb v4.4-2.6.23-rc3-common-1. + +2007-08-24 Jay Lan + + * kdb/kdba_bt_x86.c: + * retint_kernel is only defined for CONFIG_PREEMPT. + * Handle assembler code for CONFIG_HIBERNATION=y. + * Handle assembler code for CONFIG_MATH_EMULATION=y. + * Handle assembler code for CONFIG_XEN=y. + * Handle assembler code for CONFIG_KPROBES=y. + * Add CC version to the bb_all header. + * Handle spurious label in jprobe_return. + * Handle stack switch in jprobe_return. + * Prefix register name with '%' in xadd/xchg temporary variable. + * Require bb_usage_mov() to handle all the special cases internally. + * Handle stack manipulation for kexec. + * Handle spurious label in kretprobe_trampoline_holder. + * Add instructions clgi, invlpga, rcl, rdpmc, stgi, vmclear, + * vmlaunch, vmload, vmptrld, vmread, vmresume, vmrun, vmwrite, + * xstore-rng. + * Exclude more 16 bit and/or real mode acpi functions from bb_all. + * Handle assembler stack switching code in i386 do_softirq. + * kdb/kdbmain.c: + * Add CC version to the summary output. + * Bump debug_kmalloc pool from 128K to 256K, some of the kernel + * functions have huge numbers of basic blocks and jumps between them. + * Correct reinstallation of breakpoints when exiting KDB. + * Keith Owens. + * kdb v4.4-2.6.23-rc2-common-2. + +2007-08-07 Jay Lan + + * kdb v4.4-2.6.23-rc2-common-1. + +2007-08-03 Keith Owens + + * kdba_bt_x86.c: Rename some variables to make the code more readable. + Print more debug information when merging register states and when + calculating the new stack pointer. + * kdb v4.4-2.6.23-rc1-common-2. + +2007-07-30 Keith Owens + + * kdb v4.4-2.6.23-rc1-common-1. + +2007-07-26 Keith Owens + + * New x86 backtrace code. + * kdb v4.4-2.6.22-common-4. + +2007-07-17 Keith Owens + + * Make kdb_printf_lock an irq lock to keep lockdep happy. + * kdb v4.4-2.6.22-common-3. + +2007-07-13 Keith Owens + + * Increase the size of the debug_alloc pool. + * Add the caller that obtained each entry in the debug_alloc pool. + * Poison entries in the debug_alloc pool. + * Track current and maximum usage in debug_alloc pool. + * Print the debug_alloc entries that are still in use when kdb exits + (memory leaks). + * Increase the default value of BTARGS to 9. + * kdb v4.4-2.6.22-common-2. + +2007-07-09 Keith Owens + + * kdb v4.4-2.6.22-common-1. + +2007-07-02 Keith Owens + + * kdb v4.4-2.6.22-rc7-common-1. + +2007-06-20 Keith Owens + + * kdb v4.4-2.6.22-rc5-common-1. + +2007-06-15 Keith Owens + + * Do not include asm/kdb.h unless CONFIG_KDB is on. Dave Jiang. + * kdb v4.4-2.6.22-rc4-common-2. + +2007-06-08 Keith Owens + + * kdb v4.4-2.6.22-rc4-common-1. + +2007-05-28 Keith Owens + + * kdb v4.4-2.6.22-rc3-common-1. + +2007-05-22 Keith Owens + + * kdb v4.4-2.6.22-rc2-common-1. + +2007-05-22 Keith Owens + + * kdb v4.4-2.6.22-rc1-common-1. + +2007-05-17 Keith Owens + + * Add rdmsr and wrmsr commands for i386 and x86_64. Original patch by + Bernardo Innocenti for i386, reworked by Keith Owens to make it safe + on all cpu models and to handle both i386 and x86_64. + * kdb v4.4-2.6.21-common-3. + +2007-05-15 Keith Owens + + * Correct alignment of debug_alloc_header. + * kdb v4.4-2.6.21-common-2. + +2007-04-29 Keith Owens + + * kdb v4.4-2.6.21-common-1. + +2007-04-16 Keith Owens + + * Remove dead symbol declarations. + * kdb v4.4-2.6.21-rc7-common-2. + +2007-04-16 Keith Owens + + * kdb v4.4-2.6.21-rc7-common-1. + +2007-04-10 Keith Owens + + * kdb v4.4-2.6.21-rc6-common-1. + +2007-04-02 Keith Owens + + * kdb v4.4-2.6.21-rc5-common-1. + +2007-03-19 Keith Owens + + * kdb v4.4-2.6.21-rc4-common-1. + +2007-03-14 Keith Owens + + * kdb v4.4-2.6.21-rc3-common-1. + +2007-03-14 Keith Owens + + * kdb v4.4-2.6.21-rc2-common-1. + +2007-03-01 Keith Owens + + * kdb v4.4-2.6.21-rc1-common-1. + +2007-03-01 Keith Owens + + * Remove sparse warnings. + * kdb v4.4-2.6.20-common-6. + +2007-02-27 Keith Owens + + * set_irq_regs() on entry to kdb() if they are not already set. + * kdb v4.4-2.6.20-common-5. + +2007-02-22 Keith Owens + + * Initialise struct disassemble_info in kdb_id1(). + * kdb v4.4-2.6.20-common-4. + +2007-02-16 Keith Owens + + * Clean up debug_alloc_pool code. + * kdb v4.4-2.6.20-common-3. + +2007-02-16 Keith Owens + + * Initialise variable bits of struct disassemble_info each time. + * kdb v4.4-2.6.20-common-2. + +2007-02-06 Keith Owens + + * kdb v4.4-2.6.20-common-1. + +2007-02-01 Keith Owens + + * kdb v4.4-2.6.20-rc7-common-1. + +2007-01-08 Keith Owens + + * kdb v4.4-2.6.20-rc4-common-1. + +2007-01-02 Keith Owens + + * kdb v4.4-2.6.20-rc3-common-1. + +2006-12-21 Keith Owens + + * Initialize the debug_kmalloc pool on the first call, so it can be + used at any time. + * kdb v4.4-2.6.20-rc1-common-2. + +2006-12-20 Keith Owens + + * kdb v4.4-2.6.20-rc1-common-1. + +2006-11-30 Keith Owens + + * kdb v4.4-2.6.19-common-1. + +2006-11-30 Keith Owens + + * Do not access registers if kdb_current_regs is NULL. + * kdb v4.4-2.6.19-rc6-common-3. + +2006-11-27 Keith Owens + + * Only use VT keyboard if the command line allows it and ACPI indicates + that there is an i8042. + * Optimize kdb_read() to reduce the risk of dropping input characters. + * Print cpumasks as lists instead of hex, also cope with long lists. + * kdb v4.4-2.6.19-rc6-common-2. + +2006-11-20 Keith Owens + + * kdb v4.4-2.6.19-rc6-common-1. + +2006-11-09 Keith Owens + + * Change kdb() to fastcall. + * Correct loop in kdb_help(). Georg Nikodym. + * Only use VT console if the command line allows it. + * kdb v4.4-2.6.19-rc5-common-2. + +2006-11-08 Keith Owens + + * kdb v4.4-2.6.19-rc5-common-1. + +2006-11-01 Keith Owens + + * kdb v4.4-2.6.19-rc4-common-1. + +2006-10-24 Keith Owens + + * kdb v4.4-2.6.19-rc3-common-1. + +2006-10-24 Keith Owens + + * Remove redundant regs and envp parameters. + * kdb v4.4-2.6.19-rc2-common-2. + +2006-10-18 Keith Owens + + * kdb v4.4-2.6.19-rc2-common-1. + +2006-10-11 Keith Owens + + * Move kdbm_x86.c from the i386 to the common KDB patch. + * Expand kdbm_x86.c to work on x86_64 as well as i386. + * kdb v4.4-2.6.19-rc1-common-2. + +2006-10-09 Keith Owens + + * kdb v4.4-2.6.19-rc1-common-1. + +2006-10-06 Keith Owens + + * Remove #include + * kdb v4.4-2.6.18-common-2. + +2006-09-20 Keith Owens + + * kdb v4.4-2.6.18-common-1. + +2006-09-15 Keith Owens + + * kdb v4.4-2.6.18-rc7-common-1. + +2006-08-29 Keith Owens + + * Rewrite all backtrace code. + * kdb v4.4-2.6.18-rc5-common-2. + +2006-08-28 Keith Owens + + * kdb v4.4-2.6.18-rc5-common-1. + +2006-08-08 Keith Owens + + * kdb v4.4-2.6.18-rc4-common-1. + +2006-08-04 Keith Owens + + * kdb v4.4-2.6.18-rc3-common-1. + +2006-07-18 Keith Owens + + * 8250.c locking has been fixed so there is no need to break spinlocks + for keyboard entry. + * kdb v4.4-2.6.18-rc2-common-2. + +2006-07-18 Keith Owens + + * kdb v4.4-2.6.18-rc2-common-1. + +2006-07-12 Keith Owens + + * Remove dead KDB_REASON codes. + * The main kdb() function is now always entered with interrupts + disabled, so there is no need to disable bottom halves. + * sparse cleanups. + * kdb v4.4-2.6.18-rc1-common-2. + +2006-07-07 Keith Owens + + * kdb v4.4-2.6.18-rc1-common-1. + +2006-07-04 Keith Owens + + * Add KDB_REASON_CPU_UP and callbacks for cpus coming online. + * Relegate KDB_REASON_SILENT to KDB internal use only. + * Backout the v4.4-2.6.15-common-3 change that made KDB_REASON_SILENT + wait for cpus, the Dell Xeon problem has been fixed. + * notify_die() is not called for KDB_REASON_SILENT nor + KDB_REASON_CPU_UP, these events do not stay in KDB. + * Export kdb_current_task for kdbm_x86. SuSE patch + kdb-missing-export.diff + * Scale kdb_wait_for_cpus_secs by the number of online cpus. + * Delete kdb_enablehwfault, architectures now do their own setup. + * Delete kdba_enable_mce, architectures now do their own setup. + * Delete kdba_enable_lbr, kdba_disable_lbr, kdba_print_lbr, + page_fault_mca. Only ever implemented on x86, difficult to maintain + and rarely used in the field. + * Replace #ifdef KDB_HAVE_LONGJMP with #ifdef kdba_setjmp. + * kdb v4.4-2.6.17-common-2. + +2006-06-19 Keith Owens + + * kdb v4.4-2.6.17-common-1. + +2006-05-31 Keith Owens + + * Break spinlocks for keyboard entry. Hopefully a temporary hack while + I track down why keyboard entry to KDB is hanging. + * kdb v4.4-2.6.17-rc5-common-2. + +2006-05-25 Keith Owens + + * kdb v4.4-2.6.17-rc5-common-1. + +2006-05-15 Keith Owens + + * Refresh bfd related files from binutils 2.16.91.0.2. + * kdb v4.4-2.6.17-rc4-common-2. + +2006-05-12 Keith Owens + + * kdb v4.4-2.6.17-rc4-common-1. + +2006-04-28 Keith Owens + + * kdb v4.4-2.6.17-rc3-common-1. + +2006-04-22 Keith Owens + + * kdb v4.4-2.6.17-rc2-common-1. + +2006-04-11 Keith Owens + + * kdb v4.4-2.6.17-rc1-common-1. + +2006-04-05 Keith Owens + + * More fixes for the timing race with KDB_ENTER_SLAVE. + * kdb v4.4-2.6.16-common-5. + +2006-03-30 Keith Owens + + * Some code was testing KDB_IS_RUNNING() twice, which left it open to + races. Cache the result instead. + * kdb v4.4-2.6.16-common-4. + +2006-03-30 Keith Owens + + * Change CONFIG_LKCD to CONFIG_LKCD_DUMP. + * kdb v4.4-2.6.16-common-3. + +2006-03-22 Keith Owens + + * Add some more xpc flags. Dean Nelson, SGI. + * Replace open coded counter references with atomic_read(). + * Pass early_uart_console to early_uart_setup(). Francois + Wellenreiter, Bull. + * Replace open code with for_each_online_cpu(). + * If cpus do not come into kdb after a few seconds then let + architectures send a more forceful interrupt. + * Close a timing race with KDB_ENTER_SLAVE. + * kdb v4.4-2.6.16-common-2. + +2006-03-21 Keith Owens + + * kdb v4.4-2.6.16-common-1. + +2006-03-14 Nathan Scott + + * kdb v4.4-2.6.16-rc6-common-1. + +2006-02-28 Nathan Scott + + * kdb v4.4-2.6.16-rc5-common-1. + +2006-02-20 Nathan Scott + + * kdb v4.4-2.6.16-rc4-common-1. + +2006-02-06 Keith Owens + + * Change CONFIG_CRASH_DUMP to CONFIG_LKCD. + * Remove obsolete kdb_notifier_list. + * kdb v4.4-2.6.16-rc2-common-2. + +2006-02-06 Keith Owens + + * Add xpcusers command. Dean Nelson, SGI. + * kdb v4.4-2.6.16-rc2-common-1. + +2006-02-02 Keith Owens + + * Check if we have a console before using it for KDB. + * kdb v4.4-2.6.16-rc1-common-3. + +2006-02-01 Keith Owens + + * Add option 'R' to the pid command to reset to the original task. + * Include 'pid R' in archkdb* commands to reset up the original failing + task. Users may have switched to other cpus and/or tasks before + issuing archkdb. + * Compile fix for kdbm_pg.c on i386. + * kdb v4.4-2.6.16-rc1-common-2. + +2006-01-18 Keith Owens + + * kdb v4.4-2.6.16-rc1-common-1. + +2006-01-11 Keith Owens + + * Plug a timing race between KDB_ENTER_SLAVE and KDB_ENTER, and allow + the cpu command to switch to a slave cpu. + * KDB_REASON_SILENT now waits for other cpus, to avoid spurious NMI + events that were seen on some Xeon systems. + * kdb v4.4-2.6.15-common-3. + +2006-01-08 Keith Owens + + * kdb mainline invokes DIE_KDEBUG_ENTER and DIE_KDEBUG_LEAVE via + notify_die. + * Move xpc debug support from xpc to mainline kdb. + * kdbm_cm.c: check if file_lock_operations or lock_manager_operations + are set before dereferencing them. Felix Blyakher, SGI. + * kdb v4.4-2.6.15-common-2. + +2006-01-04 Keith Owens + + * Print all buffers on a page in inode pages and update formatting to be + legible, too. David Chinner, SGI. + * Update page flags in kdbm_pg. + * Remove inline from *.c files. + * kdb v4.4-2.6.15-common-1. + +2005-12-25 Keith Owens + + * kdb v4.4-2.6.15-rc7-common-1. + +2005-12-20 Keith Owens + + * kdb v4.4-2.6.15-rc6-common-1. + +2005-12-10 Keith Owens + + * Update mapping of flags to strings in kdbm_pg.c and kdbm_vm.c. + * kdb v4.4-2.6.15-rc5-common-3. + +2005-12-06 Keith Owens + + * Add RECOVERY flag to global KDB flags. + * Add kdb_{save,restore}_flags. + * kdb v4.4-2.6.15-rc5-common-2. + +2005-12-05 Keith Owens + + * kdb v4.4-2.6.15-rc5-common-1. + +2005-12-02 Keith Owens + + * kdbm_vm.c: offsets of page macros should be unsigned long. Reported + by Dean Nelson, SGI. + * kdb v4.4-2.6.15-rc4-common-1. + +2005-11-30 Keith Owens + + * New follow_page() API. + * kdb v4.4-2.6.15-rc3-common-1. + +2005-11-21 Keith Owens + + * kdb v4.4-2.6.15-rc2-common-1. + +2005-11-15 Keith Owens + + * kdb v4.4-2.6.15-rc1-common-1. + +2005-11-15 Keith Owens + + * Allow kdb_printf() to be used outside kdb, in preemptible context. + * Build with CONFIG_SWAP=n. Reported by Leo Yuriev. + * kdb v4.4-2.6.14-common-2. + +2005-10-28 Keith Owens + + * kdb v4.4-2.6.14-common-1. + +2005-10-21 Keith Owens + + * kdb v4.4-2.6.14-rc5-common-1. + +2005-10-11 Keith Owens + + * Handle removal of USB keyboard. Aaron Young, SGI. + * kdb v4.4-2.6.14-rc4-common-1. + +2005-10-05 Keith Owens + + * Extend kdb_notifier_list() codes to include dumping. + * Use emergency_restart() for reboot, it can be called from interrupt + context, unlike machine_restart(). + * kdb v4.4-2.6.14-rc3-common-1. + +2005-09-21 Keith Owens + + * Support kdb_current_task in register display and modify commands. + * Document what changes kdb's notion of the current task. + * Update rd documentation for IA64. + * Move some definictions to kdbprivate.h and remove some unused symbol + exports. + * kdb v4.4-2.6.14-rc2-common-1. + +2005-09-20 Keith Owens + + * Document IA64 handlers command. + * Add more fields to the task command. + * Cope with MCA/INIT handlers in the ps command. + * Namespace cleanup, delete unused exports, make some functions static. + * Add a kdb_notifier_list callback when kdb is about to reboot the + system. + * kdb v4.4-2.6.14-rc1-common-1. + +2005-08-29 Keith Owens + + * kdb v4.4-2.6.13-common-1. + +2005-08-24 Keith Owens + + * kdb v4.4-2.6.13-rc7-common-1. + +2005-08-08 Keith Owens + + * kdb v4.4-2.6.13-rc6-common-1. + +2005-08-02 Keith Owens + + * Print more fields from filp, dentry. + * Add kdb=on-nokey to suppress kdb entry from the keyboard. + * kdb v4.4-2.6.13-rc5-common-1. + +2005-07-30 Keith Owens + + * kdb v4.4-2.6.13-rc4-common-1. + +2005-07-26 Keith Owens + + * Fix compile problem with CONFIG_USB_KBD. + * kdb v4.4-2.6.13-rc3-common-3. + +2005-07-22 Keith Owens + + * The asmlinkage kdb() patch was lost during packaging. Reinstate it. + * kdb v4.4-2.6.13-rc3-common-2. + +2005-07-19 Keith Owens + + * Add support for USB keyboard (OHCI only). Aaron Young, SGI. + * kdb v4.4-2.6.13-rc3-common-1. + +2005-07-08 Keith Owens + + * kdb v4.4-2.6.13-rc2-common-1. + +2005-07-01 Keith Owens + + * Make kdb() asmlinkage to avoid problems with CONFIG_REGPARM. + * Change some uses of smp_processor_id() to be preempt safe. + * Use DEFINE_SPINLOCK(). + * kdb v4.4-2.6.13-rc1-common-1. + +2005-06-18 Keith Owens + + * kdb v4.4-2.6.12-common-1. + +2005-06-08 Keith Owens + + * Correct early exit from bd *. + * kdb v4.4-2.6.12-rc6-common-1. + +2005-05-25 Keith Owens + + * Delete Documentation/kdb/dump.txt. lkcd now has reasonable + integration with kdb. + * kdb v4.4-2.6.12-rc5-common-1. + +2005-05-08 Keith Owens + + * kdb v4.4-2.6.12-rc4-common-1. + +2005-04-21 Keith Owens + + * Add rpte command (find the pte for a physical page). + * kdb v4.4-2.6.12-rc3-common-1. + +2005-04-06 Keith Owens + + * Add rq and rqa commands. John Hawkes, SGI. + * kdb v4.4-2.6.12-rc2-common-1. + +2005-03-29 Keith Owens + + * Use register_sysctl_table() instead of patching kernel/sysctl.c. + * Non-ASCII characters are not printable. + * kdb v4.4-2.6.12-rc1-common-1. + +2005-03-15 Keith Owens + + * More coexistence patches for lkcd. Jason Uhlenkott, SGI. + * kdb v4.4-2.6.11-common-3. + +2005-03-08 Keith Owens + + * Coexistence patches for lkcd. Jason Uhlenkott, SGI. + * kdb v4.4-2.6.11-common-2. + +2005-03-03 Keith Owens + + * Add kdb to drivers/serial/8250_early.c. Francois Wellenreiter, Bull. + * kdb v4.4-2.6.11-common-1. + +2005-02-14 Keith Owens + + * kdb v4.4-2.6.11-rc4-common-1. + +2005-02-08 Keith Owens + + * kdb v4.4-2.6.11-rc3-bk4-common-1. + +2005-02-03 Keith Owens + + * Print more superblock fields. Nathan Scott, SGI. + * Remove kallsyms correction for modules, Linus took it. + * kdb v4.4-2.6.11-rc3-common-1. + +2005-01-27 Keith Owens + + * Add bio command. Nathan Scott, SGI. + * kdb v4.4-2.6.11-rc2-common-1. + +2005-01-20 Keith Owens + + * Include kallsyms correction for modules until Linus takes it. + * kdb v4.4-2.6.11-rc1-bk7-common-1. + +2005-01-12 Keith Owens + + * kallsyms now supports all symbols properly, remove kdb patch. + * Add last ditch allocator for debugging. + * Update kdb_meminfo_read_proc() for vmalloc changes. + * Update kdbm_vm.c for 4 level page tables. + * kdb v4.4-2.6.11-rc1-common-1. + +2004-12-25 Keith Owens + + * Add kobject command. + * Ignore low addresses and large offsets in kdbnearsym(). + * Console updates for sn2 simulator. + * kdb v4.4-2.6.10-common-1. + +2004-12-07 Keith Owens + + * kdb v4.4-2.6.10-rc3-common-1. + +2004-11-23 Keith Owens + + * Remove warning message from kdb_get_one_user_page(), it was too noisy. + * kdb v4.4-2.6.10-rc2-common-1. + +2004-11-02 Keith Owens + + * Build with kdb patch applied but CONFIG_KDB=n. + * kdb v4.4-2.6.10-rc1-common-2. + +2004-10-29 Keith Owens + + * Handle new compression scheme for kallsyms. + * Handle move of DEAD and ZOMBIE for task->state to task->exit_state. + * Tweak the concept of a valid kernel address to get all symbols, + including the symbols in the ia64 gate page. + * kdb v4.4-2.6.10-rc1-common-1. + +2004-10-21 Keith Owens + + * Handle variable size for the kernel log buffer. + * kdb v4.4-2.6.9-common-2. + +2004-10-19 Keith Owens + + * kdb v4.4-2.6.9-common-1. + +2004-10-12 Keith Owens + + * kdb v4.4-2.6.9-rc4-common-1. + +2004-10-01 Keith Owens + + * kdb v4.4-2.6.9-rc3-common-1. + +2004-09-30 Keith Owens + + * Add stackdepth command to Documentation/kdb/kdb.mm. stackdepth is + only supported on i386 and ia64 at the moment. + * Skip kdbm_pg memmap build on x86_64. Scott Lurndal, 3leafnetworks. + * Export kdb_serial_str for modular I/O. Bryan Cardillo, UPenn. + * Reinstate tab completion for symbols. + * kdb v4.4-2.6.9-rc2-common-2. + +2004-09-14 Keith Owens + + * Add task states C (traCed) and E (dEad). + * kdb v4.4-2.6.9-rc2-common-1. + +2004-08-27 Keith Owens + + * kdb v4.4-2.6.9-rc1-common-1. + +2004-08-14 Keith Owens + + * kdb v4.4-2.6.8-common-1. + +2004-08-12 Keith Owens + + * kdb v4.4-2.6.8-rc4-common-1. + +2004-08-05 Keith Owens + + * Mark kdb_initcall as __attribute_used__ for newer gcc. + * kdb v4.4-2.6.8-rc3-common-2. + +2004-08-04 Keith Owens + + * Add mdp (memory display physical) comnmand. + Ananth N Mavinakayanahalli, IBM. + * kdb v4.4-2.6.8-rc3-common-1. + +2004-07-18 Keith Owens + + * Patch for new sn_console. Erik Jacobson. SGI. + * kdb v4.4-2.6.8-rc2-common-1. + +2004-07-12 Keith Owens + + * Convert kdbm_task to standard cpumask_t. + * Document '*' (all breakpoints) option on bd/be/bc commands. + * kdb v4.4-2.6.8-rc1-common-1. + +2004-06-30 Keith Owens + + * Common changes to help the x86-64 port. + * kdb v4.4-2.6.7-common-3. + +2004-06-20 Keith Owens + + * Move kdb includes in mm/swapfile.c to reduce conflicts with other + SGI patches. + * kdb v4.4-2.6.7-common-2. + +2004-06-16 Keith Owens + + * kdb v4.4-2.6.7-common-1. + +2004-06-09 Keith Owens + + * kdb v4.4-2.6.7-rc3-common-1. + +2004-06-09 Keith Owens + + * Namespace clean up. Mark code/variables as static when it is only + used in one file, delete dead code/variables. + * Saved interrupt state requires long, not int. + * kdb v4.4-2.6.7-rc2-common-3. + +2004-06-08 Keith Owens + + * Whitespace clean up, no code changes. + * kdb v4.4-2.6.7-rc2-common-2. + +2004-06-07 Keith Owens + + * kdb v4.4-2.6.7-rc2-common-1. + +2004-06-06 Keith Owens + + * Avoid recursion problems in kdb_init(). + * Add standard archkdb commands. + * Add per_cpu command. + * Move kdb_{get,put}userarea_size definitions to linux/kdb.h. + * kdb v4.4-2.6.6-common-2. + +2004-05-23 Keith Owens + + * Shrink the output from the cpu command. + * Add cpu state 'I', the cpu is idle. + * Add cpu state '+', some kdb data is available but the cpu is not + responding. + * Do not print tasks in state I or M by default in ps and bta commands. + * Add states I (idle task) and M (sleeping system daemon) to ps and + bta commands. + * Delete unused variables. + * Move private kdb fields from kdb.h to kdbprivate.h. + * Print 'for keyboard entry' for the special cases when KDB_ENTER() is + used to get registers. + * Move bfd.h and ansidecl.h from arch/$(ARCH)/kdb to include/asm-$(ARCH) + and remove -I arch/$(ARCH)/kdb. + * dmesg command now prints from either the start or end of dmesg, or at + an arbitrary point in the middle of the kernel log buffer. + * Sensible string dump for multi byte md commands. + * 'page' command handles ia64 correctly. + * Show some activity when waiting for cpus to enter kdb. + * Change the KDB entry code to KDB. + * Allow comment commands, starting with '#'. + * Commands defined using defcmd from kdb_cmds are not printed as they + are entered, use defcmd with no parameters to print all the defined + commands. + * Add summary command. + * Update copyright notices. + * Zero suppression on md command. + * Make set NOSECT=1 the default. + * PPC64 uses OF-stdout instead of console. Ananth N Mavinakayanahalli. + * kdb v4.4-2.6.6-common-1. + +2004-05-10 Keith Owens + + * kdb v4.3-2.6.6-common-1. + +2004-05-06 Keith Owens + + * kdb v4.3-2.6.6-rc3-common-1. + +2004-05-06 Keith Owens + + * kdb v4.3-2.6.6-rc2-common-1. + +2004-04-30 Keith Owens + + * Rewrite inode_pages command for new radix code in struct page. + * kdb v4.3-2.6.6-rc1-common-1. + +2004-04-11 Keith Owens + + * Unlock sn_sal_lock before entering kdb from sn_serial. + * kdb v4.3-2.6.5-common-2. + +2004-04-05 Keith Owens + + * kdb v4.3-2.6.5-common-1. + +2004-03-22 Keith Owens + + * kdb v4.3-2.6.5-rc2-common-1. + +2004-03-12 Keith Owens + + * More work to avoid spurious messages from WARN_CONSOLE_UNLOCKED(). + * bh command bug fixes. Nathan Scott. + * kdb v4.3-2.6.4-common-1. + +2004-03-06 Keith Owens + + * Set KDB_IS_RUNNING() during kdb_init to avoid spurious messages from + WARN_CONSOLE_UNLOCKED(). + * Correct loss of symbol names in kdbnearsym. + * kdb v4.3-2.6.4-rc2-common-1. + +2004-02-29 Keith Owens + + * kdb v4.3-2.6.4-rc1-common-1. + +2004-02-21 Keith Owens + + * Correct build of kdb_cmds when using a separate object directory and + make it quiet. j-nomura (NEC), Keith Owens. + * kdb v4.3-2.6.3-common-2. + +2004-02-18 Keith Owens + + * kdb v4.3-2.6.3-common-1. + +2004-02-17 Keith Owens + + * Remove WAR for incorrect console registration patch. + * kdb v4.3-2.6.3-rc4-common-1. + +2004-02-17 Keith Owens + + * Convert longjmp buffers from static to dynamic allocation, for large + cpu counts. + * Tweak kdbm_task for SMP/UP. + * Reconcile with kdb-v4.3 2.4.25-rc1-common-1. + * Simplify coexistence with NPTL patches. + * Support kill command on new scheduler. + * Do not refetch data when printing a value as characters. + * Document the pid command. + * Work around 2.6 kallsyms 'feature'. + * Upgrade to 2.6.3-rc3. + * WAR for incorrect console registration patch. + * kdb v4.3-2.6.3-rc3-common-1. + +2003-12-03 Keith Owens + + * Reconcile 2.6-test versions from Xavier Bru (Bull), Greg Banks (SGI), + Jim Houston (Concurrent Computer Corp). + * Reconcile with kdb v4.3-2.4.23-common-2. + * Clean up CONFIG_KDB changes to {scripts,kernel}/kallsyms.c. + * Correct handling of kdb command line arguments. + * Make hooks into module code less intrusive. + * Delete kdb_active_task, not required with O(1) scheduler. + * Port kdbm_task.c from 2.4. + * Disable debug check in exit.c::next_thread() when kdb is running. + * Remove "only bh_disable when interrupts are set". BH must be disabled + in kdb to prevent deadlock on breakpoints in interrupt handlers. + * Add kdb to drivers/char/sn_serial.c. + * kdb v4.3-2.6.0-test11-common-1. + +2003-11-11 Xavier Bru + * Merge to 2.6.0-test9 +2003-10-17 Xavier Bru + * fix NUll ptr in kdb_ps at early prompt. +2003-10-14 Xavier Bru + * fix NUll ptr in kdb_ps when cpu not present. +2003-10-06 Xavier Bru + * Merge to 2.6.0-test5 + * fix compile error with CONFIG_MODULES not set. + +2003-09-08 Xavier Bru + * Merge to 2.6.0-test4 + +2003-07-10 Xavier Bru + + * Merge kdb v4.3 to 2.5.72 ia64 + * don't call local_bh_enable() with interrupts masked. + +2003-04-07 Xavier Bru + + * Merge kdb v4.1 to 2.5.64 ia64 + * new kernel parameters support + * new module format + * new kallsyms support + +2003-12-02 Keith Owens + + * Use correct page alignment in kdb_get_one_user_page(). + Prasanna S Panchamukhi, IBM. + * Split pte command into pte -m and pte -p. Dean Roe, SGI. + * kdb v4.3-2.4.23-common-2. + +2003-12-01 Keith Owens + + * kdb v4.3-2.4.23-common-1. + +2003-11-11 Keith Owens + + * Make KDB for USB keyboards build. Peter T. Breuer. + * Do not use USB keyboard if it has not been probed. + * kdb v4.3-2.4.23-rc1-common-1. + +2003-10-10 Keith Owens + + * Sync with XFS 2.4.22 tree. + * kdb v4.3-2.4.22-common-2. + +2003-08-29 Keith Owens + + * kdb v4.3-2.4.22-common-1. + +2003-07-27 Keith Owens + + * kdb v4.3-2.4.22-pre8-common-8. + +2003-07-20 Keith Owens + + * Make kdb_serial_str a common constant, the same for all consoles. + * Support SGI L1 console. + * kdb v4.3-2.4.21-common-8. + +2003-07-14 Keith Owens + + * Correct ll command. + * kdb v4.3-2.4.21-common-7. + +2003-07-08 Keith Owens + + * Export more kdb symbols. Vamsi Krishna S., IBM. + * kdb v4.3-2.4.21-common-6. + +2003-07-07 Keith Owens + + * Tweak 'waiting for cpus' message. + * kdb v4.3-2.4.21-common-5. + +2003-07-07 Keith Owens + + * 2.4.21-ia64-030702 patches common code that affects kdb. Workaround + this nuisance. + * kdb v4.3-2.4.21-common-4. + +2003-06-24 Keith Owens + + * Add task and sigset commands. Mark Goodwin, SGI. + * kdb v4.3-2.4.21-common-3. + +2003-06-23 Keith Owens + + * Sync with XFS 2.4.21 tree. + * kdb v4.3-2.4.21-common-2. + +2003-06-20 Keith Owens + + * kdb v4.3-2.4.21-common-1. + +2003-06-20 Keith Owens + + * More details on vm command, add vmp and pte commands. + Dean Nelson, Dean Roe, SGI. + * YAO1SCF (Yet Another O(1) Scheduler Coexistence Fix). + * Changes to common code to build on sparc. Tom Duffy. + * Move Tom Duffy's changes to drivers/sbus from the sparc64 + patch to the common patch to keep all the serial changes + together. + * Changes to common code to build on Xscale. Eddie Dong, Intel. + * Remove CROSS_COMPILE_INC. + * Remove obsolete boot parameter 'kdb', long since replaced by + 'kdb=on'. + * Remove obsolete kdb_eframe_t casts. + * Add CONFIG_KDB_CONTINUE_CATASTROPHIC. + * Wait a short interval for cpus to join kdb before proceeding. + * Automatically enable sysrq for sr command. + * Correct double free of kdb_printf lock, spotted by Richard Sanders. + * Add optional cpu parameter to btc command. + * kdb v4.3-2.4.20-common-1. + +2003-05-02 Keith Owens + + * Some architectures have problems with the initial empty kallsyms + section so revert to three kallsyms passes. + * Flush buffered input at startup and at 'more' prompt. + * Only print 'more' prompt when longjmp data is available. + * Print more data for buffers and inodes. + * Disable kill command when O(1) scheduler is installed, the code + needs to be redone for O(1). + * The kernel has an undocumented assumption that enable_bh() is + always called with interrupts enabled, make it so. + * Print trailing punctuation even for symbols that are not in kernel. + * Add read/write access to user pages. Vamsi Krishna S., IBM + * Rename cpu_is_online to cpu_online, as in 2.5. + * O(1) scheduler removes init_task so kdb maintains its own list of + active tasks. + * Delete btp 0 option, it needed init_tasks. + * Clean up USB keyboard support. Steven Dake. + * Sync with XFS 2.4.20 tree. + * kdb v4.2-2.4.20-common-1. + +2003-04-04 Keith Owens + + * Remove one kallsyms pass. + * Automatic detection of O(1) scheduler. + * Rename cpu_online to cpu_is_online. + * Workarounds for scheduler bugs. + * Tweak algorithm for detecting if cpu process data is available. + * Add 'kill' command. Sonic Zhang, Keith Owens. + * kdb v4.1-2.4.20-common-1. + +2003-03-16 Keith Owens + + * Each cpu saves its state as it enters kdb or before it enters code + which cannot call kdb. + * Allow btp on process 0 for a specified cpu. + * Add btt command, backtrace given a struct task address. + * btc command no longer switches cpus, instead it uses the saved data. + * bta shows the idle task on each cpu as well as real tasks, the idle + task could be handling an interrupt. + * ps command shows the idle task on each cpu. + * ps checks that the saved data for a cpu matches the process running on + that cpu and warns about stale saved data or no saved data at all. + * Remove special cases for i386 backtrace from common code and simplify + common bt code. + * Clean up kdb interaction with CONFIG_SERIAL_CONSOLE. + * Do not automatically repeat commands after the user typed 'q'. + * O(1) scheduler patch changes the process cpu field but does not set + any indicator that O(1) is being used. Adjust kdb_process_cpu() by + hand after applying O(1). + * Add kdb_print_nameval() to common code. + * Convert tests of cpu_online_map to cpu_online() macro. + * module.h needs errno.h when compiling with CONFIG_MODULES=n. + * Correct duplicate breakpoint handling. + * Do not try to send IPI during a catastrophic error, send_ipi can hang + and take kdb with it. + * kdb memmap command is i386 only, restrict it. + * Add large block device (LBD) support from XFS tree. Eric Sandeen. + * kdb v4.0-2.4.20-common-1. + +2003-02-03 Keith Owens + + * Register kdb commands early. + * Decode oops via kallsyms if it is available. + * Update copyright notices to 2003. + * Add defcmd/endefcmd to allow users to package their own macros. + * kdb commands that fail are ignored when prefixed with '-'. + * Add selection options to bta command. + * Add btc command (switch to each cpu and backtrace). + * Do real time detection of dead cpus. + * Clear ip adjusted flag when leaving kdb. + * Clean up ps command. + * Print ps output for each task when backtracing. + * Bump to version v3.0 to reduce confusion between kdb and kernel + version numbers. + * Add kdba_local_arch_setup/kdba_local_arch_cleanup to correct + keyboard freeze. Ashish Kalra. + * Refuse multiple breakpoints at the same address. + * Add fl (file_lock) command, from XFS development tree. + * Correct inode_pages, from XFS development tree. + * Add command history and editing. Sonic Zhang. + * Extend command history and editing to handle vt100 escape sequences. + * Allow tab completion at start of line. + * Touch nmi watchdog on long running bta and btc commands. + * Clean up ps output and standardize with bta codes. + * Correctly handle escaped characters in commands. + * Update man pages for btc and command history/editing. + * kdb v3.0-2.4.20-common-1. + +2002-11-29 Keith Owens + + * Upgrade to 2.4.20. + * Correct Documentation/kdb/kdb_sr.man. + * Remove leading zeroes from pids, they are decimal, not octal. + * kdb v2.5-2.4.20-common-1. + +2002-11-14 Keith Owens + + * Upgrade to 2.4.20-rc1. + * kdb v2.5-2.4.20-rc1-common-1. + +2002-11-14 Keith Owens + + * Fix processing with O(1) scheduler. + * 'go' switches back to initial cpu first. + * 'go
' only allowed on initial cpu. + * 'go' installs the global breakpoints from the initial cpu before + releasing the other cpus. + * If 'go' has to single step over a breakpoint then it single steps just + the initial cpu, installs the global breakpoints then releases the + other cpus. + * General clean up of handling for breakpoints and single stepping over + software breakpoints. + * Add kdb_notifier_block so other code can tell when kdb is in control. + * kdb v2.5-2.4.19-common-1. + +2002-11-02 Keith Owens + + * Correct build without CONFIG_KDB. + * kdb v2.4-2.4.19-common-3. + +2002-11-01 Keith Owens + + * Minimize differences from 2.5.44. + * kdb v2.4-2.4.19-common-2. + +2002-10-31 Keith Owens + + * Add defcmd/endefcmd feature. + * Remove kdb_eframe_t. + * Clear bp data before using. + * Sanity check if we have pt_regs. + * Force LINES > 1. + * Remove special case for KDB_REASON_PANIC, use KDB_ENTER() instead. + * Remove kdba_getcurrentframe(). + * Coexist with O(1) scheduler. + * Add lines option to dmesg, speed up dmesg. + * kdb v2.4-2.4.19-common-1. + +2002-10-17 Keith Owens + + * Add selection critera to ps and bta commands. + * kdb v2.3-2.4.19-common-4. + +2002-10-07 Keith Owens + + * New man page, Documentation/kdb/kdb_sr.man. + +2002-10-04 Keith Owens + + * Minimize differences between patches for 2.4 and 2.5 kernels. + * Add Configure.help for CONFIG_KDB_USB. + * Reduce stack usage. + * kdb v2.3-2.4.19-common-3. + +2002-08-10 Keith Owens + + * Replace kdb_port with kdb_serial to support memory mapped I/O. + David Mosberger. + * kdb v2.3-2.4.19-common-2. + +2002-08-07 Keith Owens + + * Upgrade to 2.4.19. + * Remove individual SGI copyrights, the general SGI copyright applies. + * Handle md0. Reported by Hugh Dickins, different fix by Keith Owens. + * Use page_address() in kdbm_pg.c. Hugh Dickins. + * Remove debugging printk from kdbm_pg.c. Hugh Dickins. + * Move breakpoint address verification into arch dependent code. + * Dynamically resize kdb command table as required. + * Common code to support USB keyboard. Sebastien Lelarge. + * kdb v2.3-2.4.19-common-1. + +2002-07-09 Keith Owens + + * Upgrade to 2.4.19-rc1. + * Add dmesg command. + * Clean up copyrights, Eric Sandeen. + * kdb v2.2-2.4.19-rc1-common-1. + +2002-06-14 Keith Owens + + * Upgrade to 2.4.19-pre10. + * Sync with XFS. + * kdb v2.1-2.4.19-pre10-common-1. + +2002-04-09 Keith Owens + + * Upgrade to 2.4.19-pre6. + * kdb v2.1-2.4.19-pre6-common-1. + +2002-03-18 Keith Owens + + * Syntax check mdWcN commands. + +2002-03-01 Keith Owens + + * Sync with XFS 2.4.18. + * kdb v2.1-2.4.18-common-2. + +2002-02-26 Keith Owens + + * Upgrade to 2.4.18. + * Add Paul Dorwin (IBM) magicpoint slides on using kdb as + Documentation/kdb/slides. + * kdb v2.1-2.4.18-common-1. + +2002-01-23 Keith Owens + + * Sync with XFS pagebuf changes. + * kdb v2.1-2.4.17-common-2. + +2002-01-18 Keith Owens + + * Ignore single stepping during panic. + * Remove kdba_getword, kdba_putword. Replace with kdb_getword, + kdb_putword that rely on copy_xx_user. The new functions return + an error code, like copy_xx_user. + * New functions kdb_getarea, kdb_putarea for copying areas of data + such as structures. These functions also return an error code. + * Change all common code to use the new functions. + * bp command checks that it can read and write the word at the + breakpoint before accepting the address. + * Break points are now set FIFO and cleared LIFO so overlapping + entries give sensible results. + * Verify address before disassembling code. + * Common changes for sparc64. Ethan Solomita, Tom Duffy. + * Remove ss , never supported. + * Remove kallsyms entries from arch vmlinux.lds files. + * Specify which commands auto repeat. + * kdb v2.1-2.4.17-common-1. + +2002-01-07 Keith Owens + + * Remove console semaphore code, not good in interrupt. + * Remove fragment of ia64 patch that had crept into kdb. + * Release as kdb v2.0-2.4.17-common-3. + +2002-01-04 Keith Owens + + * Sync xfs <-> kdb common code. + +2001-12-22 Keith Owens + + * Upgrade to 2.4.17. + * Clean up ifdef CONFIG_KDB. + * Add ifdef CONFIG_KDB around include kdb.h. + * Delete dummy kdb.h files for unsupported architectures. + * Delete arch i386 and ia64 specific files. This changelog now + applies to kdb common code only. + * Release as kdb v2.0-2.4.17-common-1. + +2001-12-03 Keith Owens + + * Upgrade to 2.4.16. + * Add include/asm-um/kdb.h stub to allow XFS to be tested under UML. + * Check if an interrupt frame on i386 came from user space. + * Out of scope bug fix in kdb_id.c. Ethan Solomita. + * Changes to common code to support sparc64. Ethan Solomita. + * Change GFP_KERNEL to GFP_ATOMIC in disasm. Ethan Solomita. + +2001-11-16 Keith Owens + + * Upgrade to 2.4.15-pre5. + * Wrap () around #define expressions with unary operators. + +2001-11-13 Keith Owens + + * Upgrade to 2.4.15-pre4. + * kbdm_pg.c patch from Hugh Dickins. + +2001-11-07 Keith Owens + + * Upgrade to 2.4.14-ia64-011105. + * Change name of l1 serial I/O routine, add ia64 init command. SGI. + * Sync kdbm_pg with XFS. + +2001-11-06 Keith Owens + + * Upgrade to kernel 2.4.14. + +2001-11-02 Keith Owens + + * Sync kdbm_pg.c with XFS. + +2001-10-24 Keith Owens + + * Upgrade to kernel 2.4.13. + +2001-10-14 Keith Owens + + * More use of TMPPREFIX in top level Makefile to speed up NFS compiles. + + * Correct repeat calculations in md/mds commands. + +2001-10-10 Keith Owens + + * Copy bfd.h and ansidecl.h to arch/$(ARCH)/kdb, remove dependecies on + user space includes. + + * Update kdb v1.9 to kernel 2.4.11. + +2001-10-01 Keith Owens + + * Update kdb v1.9 to kernel 2.4.11-pre1 and 2.4.10-ac1. + + * Correct loop in kdb_parse, reported by Tachino Nobuhiro. + +2001-09-25 Keith Owens + + * Update kdb v1.8 to kernel 2.4.10. + + * kdbm_pg patch from Hugh Dickens. + + * DProbes patch from Bharata B Rao. + + * mdWcn and mmW patch from Vamsi Krishna S. + + * i386 disasm layout patch from Jean-Marc Saffroy. + + * Work around for 64 bit binutils, Simon Munton. + + * kdb.mm doc correction by Chris Pascoe. + + * Enter repeats the last command, IA64 disasm only prints one + instruction. Don Dugger. + + * Allow kdb/modules to be linked into vmlinux. + + * Remove obsolete code from kdb/modules/kdbm_{pg,vm}.c. + + * Warn when commands are entered at more prompt. + + * Add MODULE_AUTHOR, DESCRIPTION, LICENSE. + + * Release as kdb v1.9. + +2001-02-27 Keith Owens + + * Update kdb v1.8 to kernel 2.4.2, sync kdb/modules with XFS. + + * Hook into panic() call. + +2000-12-18 Keith Owens + + * Update kdb v1.7 to kernel 2.4.0-test13-pre3, sync kdb/modules with + XFS. + +2000-11-18 Keith Owens + + * Update to kernel 2.4.0-test11-pre7, including forward port of + bug fixes from WIP 2.4.0-test9 tree. + + * Update to Cygnus CVS trees for disassembly code. + + * Bump to kdb v1.6. + +2000-10-19 Keith Owens + + * Update to kernel 2.4.0-test10-pre4. + +2000-10-15 Keith Owens + + * kdb/kdbmain.c (kdb_parse): Correctly handle blank input. + + * kdb/kdbmain.c (kdb_local, kdb): Reason SILENT can have NULL regs. + +2000-10-13 Keith Owens + + * kdb/kdbmain.c: Reduce CMD_LEN to avoid overflowing kdb_printf buffer. + +2000-10-11 Keith Owens + + * kdb/kdbmain.c (kdb): Test for userspace breakpoints before driving + other cpus into kdb. Speeds up gdb and avoids SMP race. + + * arch/i386/kdb/kdba_io.c (get_serial_char, get_kbd_char): Ignore + unprintable characters. + + * arch/i386/kdb/kdba_io.c (kdba_read): Better handling of buffer size. + +2000-10-04 Keith Owens + + * arch/i386/kdb/kdba_bt.c (kdba_bt_process): Verify that esp is inside + task_struct. Original patch by Mike Galbraith. + + * kdb/kdb_io.c (kdb_getstr): Reset output line counter, remove + unnecessary prompts. + + * arch/i386/kdb/kdbasupport.c (kdb_getregcontents): Change " cs" to + "xcs", ditto ss, ds, es. gdb2kdb does not like leading spaces. + + * include/asm-xxx/kdb.h: Add dummy kdb.h for all architectures except + ix86. This allows #include to appear in arch independent + code without causing compile errors. + + * kdb/modules/kdbm_pg: Sync with XFS. + +2000-10-03 Keith Owens + + * kdb/kdb_io.c (kdb_read): Ignore NMI while waiting for input. + + * kdb/kdb_io.c, kdb/Makefile: Export kdb_read. + +2000-10-02 Keith Owens + + * arch/i386/kernel/smpboot.c (do_boot_cpu): Set nmi_watchdog_source to 2 + to avoid premature NMI oops during cpu bring up. We have to assume that + a box with more than 1 cpu has a working IO-APIC. + + * Documentation/kdb/{kdb.mm,kdb_md.man}: Add mdr command. + + * kdb/kdbmain.c (kdb_md): Add mdr command. + + * Release as kdb v1.5 against 2.4.0-test9-pre8. + + * arch/i386/kdb/kdba_io.c, arch/i386/kdb/kdbasupport.c, kdb/kdbmain.c, + kdb/kdb_io.c, kdb/kdb_id.c: Remove zero initializers for static + variables. + +2000-09-28 Keith Owens + + * various: Add nmi_watchdog_source, 1 local APIC, 2 IO-APIC. + Test nmi_watchdog_source instead of nr_ioapics so UP works on SMP hardware. + + * arch/i386/kernel/io_apic.c: Rename setup_nmi to setup_nmi_io for clarity. + + * kdb/kdbmain.c (kdb_parse): Only set NO_WATCHDOG if it was already set. + + * kdb/kdbmain.c (kdb): Clear NO_WATCHDOG on all exit paths. + + * include/linux/kdb.h: Add KDB_REASON_SILENT. + + * kdb/kdbmain.c (kdb_local): Treat reason SILENT as immediate 'go'. + + * kdb/kdbmain.c (kdb_init): Invoke kdb with reason SILENT to instantiate + any breakpoints on boot cpu. + + * arch/i386/kernel/smpboot.c (smp_callin): Invoke kdb with reason SILENT + to instantiate any global breakpoints on this cpu. + + * kdb/kdb_cmds: Remove comment that said initial commands only worked on + boot cpu. + +2000-09-27 Keith Owens + + * arch/i386/kernel/msr.c: Move {rd,wr}msr_eio to include/asm-i386/apic.h. + + * include/asm-i386/apic.h: Define NMI interfaces. + + * kernel/sysctl.c (kern_table): + * kernel/sysctl.c (do_proc_set_nmi_watchdog): + Add /proc/sys/kernel/nmi_watchdog. + + * arch/i386/kernel/apic.c: New routines set_nmi_counter_local, + setup_apic_nmi_watchdog. + + * arch/i386/kernel/traps.c: New routine set_nmi_watchdog(). Call apic + routines to set/clear local apic timer. + +2000-09-26 Keith Owens + + * include/linux/sysctl.h (enum): Add NMI_WATCHDOG. + + * arch/i386/kernel/traps.c (nmi_watchdog_tick): Check nmi_watchdog is + still on. + + * arch/i386/config.in: Add CONFIG_UP_NMI_WATCHDOG. + + * Documentation/Configure.help: Add CONFIG_UP_NMI_WATCHDOG. + + * Documentation/nmi_watchdog.txt: Update for UP NMI watchdog. + +2000-09-25 Keith Owens + + * arch/i386/kernel/apic.c (init_apic_mappings): + * arch/i386/kernel/io_apic.c (IO_APIC_init_uniprocessor): + Merge Keir Fraser's local APIC for uniprocessors patch. + +2000-09-24 Keith Owens + + * Various: Declare initialization routines as __init. + + * Makefile: Define and export AWK. + + * kdb/Makefile: Generate gen-kdb_cmds.c from kdb/kdb_cmds. + + * kdb/kdbmain.c (kdb_init): Call new routine kdb_cmds_init to execute + whatever the user put in kdb/kdb_cmds. + + * arch/i386/kdb/kdba_bt.c (kdba_bt_stack): New parameter to + indicate if esp in regs is known to be valid or not. + + * kdb/kdb_bp.c, arch/i386/kdb/kdba_bp.c: More trace prints for + breakpoint handling. + + * arch/i386/kdb/kdba_bp.c (kdba_installbp): Finally found and fixed the + annoying breakpoint bug where breakpoints where not always installed + after 'go'. + + * Documentation/kdb: Update man pages kdb.mm, kdb_env.man, kdb_ss.man. + + * Released as kdb-v1.5-beta1-2.4.0-test8. + + * Sync to 2.4.0-test9-pre6 and release as kdb-v1.5-beta1-2.4.0-test9-pre6. + +2000-09-23 Keith Owens + + * arch/i386/kdb/kdbasupport.c (kdba_getregcontents): New pseudo + registers cesp and ceflags to help with debugging the debugger. + + * kdb/kdbmain.c (kdb_local, kdb): Add KDB_REASON_RECURSE. Add + environment variable RECURSE. Add code to cope with some types of + recursion. + + * kdb/kdbmain.c (kdb), arch/i386/kdba/kdba_bp.c: Add + kdba_clearsinglestep. + +2000-09-22 Keith Owens + + * drivers/video/vgacon.c (write_vga): No cli() if kdb is running, avoid + console deadlock. + + * arch/i386/kernel/irq.c (get_irqlock): Warn if kdb is running, may hang. + + * include/linux/kdb.h: Define KDB_IS_RUNNING as (0) if no CONFIG_KDB. + + * arch/i386/kdb/kdba_bt.c (kdba_bt_stack): Do not attempt a backtrace if + the code segment is not in the kernel. + + * kdb/modules: Change modules from MX_OBJS to M_OBJS. Remove EXPORT_NOSYMBOLS. + +2000-09-21 Keith Owens + + * arch/i386/kernel/i386_ksyms.c: Move EXPORT_SYMBOLS for kdb to kdb/kdbmain.c. + + * kdb/Makefile: Change kdb/kdbmain.o from O_OBJS to OX_OBJS. + + * arch/i386/kernel/smp.c: Remove some #ifdef CONFIG_KDB. Remove kdbprivate.h. + + * include/linux/kdb.h: Add kdb_print_state. Add KDB_STATE_WAIT_IPI. + + * kdb/kdbmain.c (kdb): Only mark cpu as leaving if it is in KDB state. Maintain + WAIT_IPI state so a cpu is only driven through NMI once. + + * arch/i386/kernel/smp.c (smp_kdb_stop): All state fiddling moved to kdb(). + +2000-09-20 Keith Owens + + * include/linux/kdb.h: #define kdb() as (0) if kdb is not configured. + + * arch/i386/kernel/traps.c: Remove some #ifdef CONFIG_KDB. + + * include/linux/kdbprivate.h: Move per cpu state to kdb.h. + + * include/linux/kdb.h: Add KDB_STATE_NO_WATCHDOG, KDB_STATE_PRINTF_LOCK. + Rename KDB_DEBUG_xxx to KDB_DEBUG_FLAG_xxx. Clean up debug flag + definitions. + + * arch/i386/kernel/traps.c (nmi_watchdog_tick): Check no watchdog. + + * kdb/kdbmain.c (kdb): Set no watchdog in normal kdb code. + + * kdb/kdbmain.c (kdb_parse): Allow watchdog in commands. + + * kdb/kdb_io.c (kdb_printf): No watchdog during printing. Clean up lock handling. + + * kdb/kdbmain.c (kdb_set): Clean up debug flag handling. + +2000-09-19 Juan J. Quintela + + * kdb/arch/i386/kdb/kdba_io.c: Allow kdb to compile without CONFIG_VT and/or + serial console. + +2000-09-19 Keith Owens + + * include/linux/kdb.h: Define KDB_DEBUG_STATE(). + + * kdb/kdbmain.c (kdb): Add kdb_print_state(), calls to KDB_DEBUG_STATE(). + +2000-09-16 Keith Owens + + * Move to finer grained control over individual processors in kdb with + per cpu kdb state. Needed to allow ss[b] to only release one processor, + previously ss[b] released all processors. Also need to recover from + errors inside kdb commands, e.g. oops in kdbm_pg code. + + * various: + Move global flags KDB_FLAG_SSB, KDB_FLAG_SUPRESS, KDB_FLAG_FAULT, + KDB_FLAG_SS, KDB_FLAG_SSBPT, kdb_active, to per cpu state and macros + KDB_STATE(xxx). + Replace kdb_flags & KDB_FLAG_xxx with KDB_FLAG(xxx). + Replace kdb_flags & KDB_DEBUG_xxx with KDB_DEBUG(xxx). + Replace specific tests with wrapper KDB_IS_RUNNING(). + + * various: Remove #ifdef CONFIG_SMP from kdb code wherever + possible. Simplifies the code and makes it much more readable. + + * arch/i386/kdb/kdbasupport.c (kdb_setjmp): Record if we have reliable + longjmp data instead of assuming it is always set. + + * various: Replace smp_kdb_wait with per cpu state, HOLD_CPU. + + * init/main.c : Replace #ifdef KDB_DEBUG with KDB_DEBUG(CALLBACK). + + * include/linux/kdbprivate.h: Separate command return codes from error + codes. Add more detailed command codes. + + * arch/i386/kernel/traps.c (die): Change spin_lock_irq to + spin_lock_irqsave. Why did I do this? + + * kdb/kdbmain.c (kdb_parse): Set per cpu flag CMD before executing kdb + command. More detailed return codes for commands that affect + processors. + + * kdb/kdbmain.c (kdb_previous_event): New, check if any processors are + still executing the previous kdb event. Removes a race window where a + second event could enter kdb before the first had completely ended. + + * kdb/kdbmain.c (kdb): Document all the concurrency conditions and how + kdb handles them. ss[b] now releases only the current cpu. Do not set + breakpoints when releasing for ss[b]. Recover from errors in kdb + commands. Check that we have reliable longjmp data before using it. + + * various: Update return code documentation. + + * kdb/kdb_bp.c (kdb_ss): Separate ss and ssb return codes. + + * kdb/kdbsupport.c (kdb_ipi): Finer grained algorithm for deciding + whether to call send a stop signal to a cpu. + + * arch/i386/kdb/kdba_bp.c (kdba_db_trap): Separate ss and ssb return + codes. Reinstall delayed software breakpoints per cpu instead of + globally. Changed algorithm for handling ss[b]. + + * arch/i386/kdb/kdba_bp.c (kdba_bp_trap): Match software breakpoints per + cpu instead of globally. + + * include/linux/kdb.h: Bump version to kdb v1.5. + +2000-09-16 Keith Owens + + * kernel/sysctl.c (kern_table): add /proc/sys/kernel/kdb. + + * init/main.c (parse_options): add boot flags kdb=on, kdb=off, + kdb=early. + + * include/linux/sysctl.h (enum): add KERN_KDB. + + * drivers/char/serial.c (receive_chars): check kdb_on. + + * drivers/char/keyboard.c (handle_scancode): check kdb_on. + + * arch/i386/kernel/traps.c (nmi_watchdog_tick): check kdb_on. + + * arch/i386/config.in: add CONFIG_KDB_OFF. + + * Documentation/Configure.help: add CONFIG_KDB_OFF. + + * kdb/kdbmain.c: add kdb_initial_cpu, kdb_on. + + * kdb/kdbmain.c (kdb): check kdb_on, set kdb_initial_cpu. + + * kdb/kdbmain.c (kdb_init): add Keith Owens to kdb banner. + + * kdb/kdb_io.c (kdb_printf): serialize kdb_printf output. + + * kdb/kdb_bt.c (kdb_bt): check environment variable BTAPROMPT. + + * kdb/kdbsupport.c (kdb_ipi): ignore NMI for kdb_initial_cpu. + + * kdb/modules/kdbm_pg.c (kdbm_page): merge updates from 2.4.0-test5-xfs. + + * kdb/kdb_bt.man: add btp, bta, BTAPROMPT. + + * kdb/kdb.mm: add CONFIG_KDB_OFF, boot flags, btp, bta. + + * include/linux/kdbprivate.h: add kdb_initial_cpu. + + * include/linux/kdb.h: add kdb_on, bump version to kdb v1.4. --- /dev/null +++ b/kdb/Makefile @@ -0,0 +1,43 @@ +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. +# + +CCVERSION := $(shell $(CC) -v 2>&1 | sed -ne '$$p') +obj-y := kdb_bt.o kdb_bp.o kdb_id.o kdbsupport.o gen-kdb_cmds.o kdbmain.o kdb_io.o kdbdereference.o +CFLAGS_kdbmain.o += -DCCVERSION="$(CCVERSION)" + +subdir-$(CONFIG_KDB_MODULES) := modules +obj-y += $(addsuffix /built-in.o, $(subdir-y)) + +clean-files := gen-kdb_cmds.c + +override CFLAGS := $(CFLAGS:%-pg=% ) + +# define architecture dependent kdb_cmds +ifeq ($(CONFIG_IA64),y) + KDB_CMDS = ia64/kdb/kdb_cmds +else + ifeq ($(CONFIG_X86_64),y) + KDB_CMDS = x86/kdb/kdb_cmds_64 + else + ifeq ($(CONFIG_X86_32),y) + KDB_CMDS = x86/kdb/kdb_cmds_32 + endif + endif +endif + +quiet_cmd_gen-kdb = GENKDB $@ + cmd_gen-kdb = $(AWK) 'BEGIN {print "\#include "; print "\#include "} \ + /^\#/{next} \ + /^[ \t]*$$/{next} \ + {gsub(/"/, "\\\"", $$0); \ + print "static __initdata char kdb_cmd" cmds++ "[] = \"" $$0 "\\n\";"} \ + END {print "extern char *kdb_cmds[]; char __initdata *kdb_cmds[] = {"; for (i = 0; i < cmds; ++i) {print " kdb_cmd" i ","}; print(" NULL\n};");}' \ + $(filter-out %/Makefile,$^) > $@ + +$(obj)/gen-kdb_cmds.c: $(src)/kdb_cmds $(wildcard $(TOPDIR)/arch/$(KDB_CMDS)) $(src)/Makefile + $(call cmd,gen-kdb) --- /dev/null +++ b/kdb/kdb_bp.c @@ -0,0 +1,661 @@ +/* + * Kernel Debugger Architecture Independent Breakpoint Handler + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Table of kdb_breakpoints + */ +kdb_bp_t kdb_breakpoints[KDB_MAXBPT]; + +/* + * Predicate to test whether a breakpoint should be installed + * on this CPU. + * + * Note that for purposes of installation, hardware breakpoints + * are treated as local (even if the global flag is set), on + * the assumption that the require per-cpu registers to be set. + */ + +static inline int kdb_is_installable_global_bp(const kdb_bp_t *bp) +{ + return (bp->bp_enabled && + bp->bp_global && + !bp->bp_forcehw); +} + +static int kdb_is_installable_local_bp(const kdb_bp_t *bp) +{ + if (!bp->bp_enabled) + return 0; + + if (bp->bp_forcehw) { + if (bp->bp_cpu == smp_processor_id() || bp->bp_global) + return 1; + } else { + if (bp->bp_cpu == smp_processor_id() && !bp->bp_global) + return 1; + } + return 0; +} + +/* + * kdb_bp_install_global + * + * Install global kdb_breakpoints prior to returning from the + * kernel debugger. This allows the kdb_breakpoints to be set + * upon functions that are used internally by kdb, such as + * printk(). + * + * Parameters: + * regs Execution frame. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + * This function is only called once per kdb session. + */ + +void +kdb_bp_install_global(struct pt_regs *regs) +{ + int i; + + for(i=0; ibp_enabled, bp->bp_global); + } + /* HW BP local or global are installed in kdb_bp_install_local*/ + if (kdb_is_installable_global_bp(bp)) + kdba_installbp(regs, bp); + } +} + +/* + * kdb_bp_install_local + * + * Install local kdb_breakpoints prior to returning from the + * kernel debugger. This allows the kdb_breakpoints to be set + * upon functions that are used internally by kdb, such as + * printk(). + * + * Parameters: + * regs Execution frame. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + * This function is called once per processor. + */ + +void +kdb_bp_install_local(struct pt_regs *regs) +{ + int i; + + for(i=0; ibp_enabled, bp->bp_global, + smp_processor_id(), bp->bp_cpu); + } + if (kdb_is_installable_local_bp(bp)) + kdba_installbp(regs, bp); + } +} + +/* + * kdb_bp_remove_global + * + * Remove global kdb_breakpoints upon entry to the kernel debugger. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdb_bp_remove_global(void) +{ + int i; + + for(i=KDB_MAXBPT-1; i>=0; i--) { + kdb_bp_t *bp = &kdb_breakpoints[i]; + + if (KDB_DEBUG(BP)) { + kdb_printf("kdb_bp_remove_global bp %d bp_enabled %d bp_global %d\n", + i, bp->bp_enabled, bp->bp_global); + } + if (kdb_is_installable_global_bp(bp)) + kdba_removebp(bp); + } +} + + +/* + * kdb_bp_remove_local + * + * Remove local kdb_breakpoints upon entry to the kernel debugger. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdb_bp_remove_local(void) +{ + int i; + + for(i=KDB_MAXBPT-1; i>=0; i--) { + kdb_bp_t *bp = &kdb_breakpoints[i]; + + if (KDB_DEBUG(BP)) { + kdb_printf("kdb_bp_remove_local bp %d bp_enabled %d bp_global %d cpu %d bp_cpu %d\n", + i, bp->bp_enabled, bp->bp_global, + smp_processor_id(), bp->bp_cpu); + } + if (kdb_is_installable_local_bp(bp)) + kdba_removebp(bp); + } +} + +/* + * 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) +{ + if (bp->bp_forcehw) { + kdb_printf("Forced "); + } + + if (!bp->bp_template.bph_free) { + kdb_printf("%s ", kdba_bptype(&bp->bp_template)); + } else { + kdb_printf("Instruction(i) "); + } + + kdb_printf("BP #%d at ", i); + kdb_symbol_print(bp->bp_addr, NULL, KDB_SP_DEFAULT); + + if (bp->bp_enabled) { + kdba_printbp(bp); + if (bp->bp_global) + kdb_printf(" globally"); + else + kdb_printf(" on cpu %d", bp->bp_cpu); + if (bp->bp_adjust) + kdb_printf(" adjust %d", bp->bp_adjust); + } else { + kdb_printf("\n is disabled"); + } + + kdb_printf("\taddr at %016lx, hardtype=%d, forcehw=%d, installed=%d, hard=%p\n", + bp->bp_addr, bp->bp_hardtype, bp->bp_forcehw, + bp->bp_installed, bp->bp_hard); + + kdb_printf("\n"); +} + +/* + * kdb_bp + * + * Handle the bp, and bpa commands. + * + * [bp|bpa|bph] [DATAR|DATAW|IO [length]] + * + * Parameters: + * argc Count of arguments in argv + * argv Space delimited command line arguments + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic if failure. + * Locking: + * None. + * Remarks: + * + * bp Set breakpoint. Only use hardware assist if necessary. + * bpa Set breakpoint on all cpus, only use hardware regs if necessary + * bph Set breakpoint - force hardware register + * bpha Set breakpoint on all cpus, force hardware register + */ + +static int +kdb_bp(int argc, const char **argv) +{ + int i, bpno; + kdb_bp_t *bp, *bp_check; + int diag; + int free; + char *symname = NULL; + long offset = 0ul; + int nextarg; + static kdb_bp_t kdb_bp_template; + + if (argc == 0) { + /* + * Display breakpoint table + */ + for(bpno=0,bp=kdb_breakpoints; bpnobp_free) continue; + + kdb_printbp(bp, bpno); + } + + return 0; + } + + memset(&kdb_bp_template, 0, sizeof(kdb_bp_template)); + + kdb_bp_template.bp_global = ((strcmp(argv[0], "bpa") == 0) + || (strcmp(argv[0], "bpha") == 0)); + kdb_bp_template.bp_forcehw = ((strcmp(argv[0], "bph") == 0) + || (strcmp(argv[0], "bpha") == 0)); + + /* Fix me: "bp" is treated as "bpa" to avoid system freeze. -jlan */ + if (strcmp(argv[0], "bp") == 0) + kdb_bp_template.bp_global = 1; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &kdb_bp_template.bp_addr, + &offset, &symname); + if (diag) + return diag; + if (!kdb_bp_template.bp_addr) + return KDB_BADINT; + + /* + * Find an empty bp structure, to allocate + */ + free = KDB_MAXBPT; + for(bpno=0,bp=kdb_breakpoints; bpnobp_free) { + break; + } + } + + if (bpno == KDB_MAXBPT) + return KDB_TOOMANYBPT; + + /* + * Handle architecture dependent parsing + */ + diag = kdba_parsebp(argc, argv, &nextarg, &kdb_bp_template); + if (diag) { + return diag; + } + + + /* + * Check for clashing breakpoints. + * + * Note, in this design we can't have hardware breakpoints + * enabled for both read and write on the same address, even + * though ia64 allows this. + */ + for(i=0,bp_check=kdb_breakpoints; ibp_free && + bp_check->bp_addr == kdb_bp_template.bp_addr && + (bp_check->bp_global || + bp_check->bp_cpu == kdb_bp_template.bp_cpu)) { + kdb_printf("You already have a breakpoint at " + kdb_bfd_vma_fmt0 "\n", kdb_bp_template.bp_addr); + return KDB_DUPBPT; + } + } + + kdb_bp_template.bp_enabled = 1; + + /* + * Actually allocate the breakpoint found earlier + */ + *bp = kdb_bp_template; + bp->bp_free = 0; + + if (!bp->bp_global) { + bp->bp_cpu = smp_processor_id(); + } + + /* + * Allocate a hardware breakpoint. If one is not available, + * disable the breakpoint, but leave it in the breakpoint + * table. When the breakpoint is re-enabled (via 'be'), we'll + * attempt to allocate a hardware register for it. + */ + if (!bp->bp_template.bph_free) { + kdba_alloc_hwbp(bp, &diag); + if (diag) { + bp->bp_enabled = 0; + bp->bp_hardtype = 0; + kdba_free_hwbp(bp); + return diag; + } + } + + kdb_printbp(bp, bpno); + + return 0; +} + +/* + * kdb_bc + * + * Handles the 'bc', 'be', and 'bd' commands + * + * [bd|bc|be] + * [bd|bc|be] * + * + * Parameters: + * argc Count of arguments in argv + * argv Space delimited command line arguments + * 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 + +static int +kdb_bc(int argc, const char **argv) +{ + kdb_machreg_t addr; + kdb_bp_t *bp = NULL; + int lowbp = KDB_MAXBPT; + int highbp = 0; + int done = 0; + int i; + int diag; + int cmd; /* KDBCMD_B? */ + + if (strcmp(argv[0], "be") == 0) { + cmd = KDBCMD_BE; + } else if (strcmp(argv[0], "bd") == 0) { + cmd = KDBCMD_BD; + } else + cmd = KDBCMD_BC; + + if (argc != 1) + return KDB_ARGCOUNT; + + if (strcmp(argv[1], "*") == 0) { + lowbp = 0; + highbp = KDB_MAXBPT; + } else { + diag = kdbgetularg(argv[1], &addr); + if (diag) + return diag; + + /* + * For addresses less than the maximum breakpoint number, + * assume that the breakpoint number is desired. + */ + if (addr < KDB_MAXBPT) { + bp = &kdb_breakpoints[addr]; + lowbp = highbp = addr; + highbp++; + } else { + for(i=0, bp=kdb_breakpoints; ibp_addr == addr) { + lowbp = highbp = i; + highbp++; + break; + } + } + } + } + + /* + * Now operate on the set of breakpoints matching the input + * criteria (either '*' for all, or an individual breakpoint). + */ + for(bp=&kdb_breakpoints[lowbp], i=lowbp; + i < highbp; + i++, bp++) { + if (bp->bp_free) + continue; + + done++; + + switch (cmd) { + case KDBCMD_BC: + if (bp->bp_hardtype) + kdba_free_hwbp(bp); + + bp->bp_enabled = 0; + bp->bp_global = 0; + + kdb_printf("Breakpoint %d at " kdb_bfd_vma_fmt " cleared\n", + i, bp->bp_addr); + + bp->bp_addr = 0; + bp->bp_free = 1; + + break; + case KDBCMD_BE: + /* + * Allocate a hardware breakpoint. If one is not + * available, don't enable the breakpoint. + */ + if (!bp->bp_template.bph_free + && !bp->bp_hardtype) { + kdba_alloc_hwbp(bp, &diag); + if (diag) { + bp->bp_enabled = 0; + bp->bp_hardtype = 0; + kdba_free_hwbp(bp); + return diag; + } + } + + bp->bp_enabled = 1; + + kdb_printf("Breakpoint %d at " kdb_bfd_vma_fmt " enabled", + i, bp->bp_addr); + + kdb_printf("\n"); + break; + case KDBCMD_BD: + if (!bp->bp_enabled) + break; + + /* + * Since this breakpoint is now disabled, we can + * give up the hardware register which is allocated + * to it. + */ + if (bp->bp_hardtype) + kdba_free_hwbp(bp); + + bp->bp_enabled = 0; + + kdb_printf("Breakpoint %d at " kdb_bfd_vma_fmt " disabled\n", + i, bp->bp_addr); + + break; + } + if (bp->bp_delay && (cmd == KDBCMD_BC || cmd == KDBCMD_BD)) { + bp->bp_delay = 0; + KDB_STATE_CLEAR(SSBPT); + } + } + + return (!done)?KDB_BPTNOTFOUND:0; +} + +/* + * kdb_ss + * + * Process the 'ss' (Single Step) and 'ssb' (Single Step to Branch) + * commands. + * + * ss + * ssb + * + * Parameters: + * argc Argument count + * argv Argument vector + * Outputs: + * None. + * Returns: + * KDB_CMD_SS[B] for success, a kdb error if failure. + * Locking: + * None. + * Remarks: + * + * Set the arch specific option to trigger a debug trap after the next + * 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. + */ + +static int +kdb_ss(int argc, const char **argv) +{ + int ssb = 0; + struct pt_regs *regs = get_irq_regs(); + + ssb = (strcmp(argv[0], "ssb") == 0); + if (argc != 0) + return KDB_ARGCOUNT; + + if (!regs) { + kdb_printf("%s: pt_regs not available\n", __FUNCTION__); + return KDB_BADREG; + } + + /* + * Set trace flag and go. + */ + KDB_STATE_SET(DOING_SS); + if (ssb) + KDB_STATE_SET(DOING_SSB); + + kdba_setsinglestep(regs); /* Enable single step */ + + if (ssb) + return KDB_CMD_SSB; + return KDB_CMD_SS; +} + +/* + * kdb_initbptab + * + * Initialize the breakpoint table. Register breakpoint commands. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void __init +kdb_initbptab(void) +{ + int i; + kdb_bp_t *bp; + + /* + * First time initialization. + */ + memset(&kdb_breakpoints, '\0', sizeof(kdb_breakpoints)); + + for (i=0, bp=kdb_breakpoints; ibp_free = 1; + /* + * The bph_free flag is architecturally required. It + * is set by architecture-dependent code to false (zero) + * in the event a hardware breakpoint register is required + * for this breakpoint. + * + * The rest of the template is reserved to the architecture + * dependent code and _must_ not be touched by the architecture + * independent code. + */ + bp->bp_template.bph_free = 1; + } + + kdb_register_repeat("bp", kdb_bp, "[]", "Set/Display breakpoints", 0, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("bl", kdb_bp, "[]", "Display breakpoints", 0, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("bpa", kdb_bp, "[]", "Set/Display global breakpoints", 0, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("bph", kdb_bp, "[]", "Set hardware breakpoint", 0, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("bpha", kdb_bp, "[]", "Set global hardware breakpoint", 0, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("bc", kdb_bc, "", "Clear Breakpoint", 0, KDB_REPEAT_NONE); + kdb_register_repeat("be", kdb_bc, "", "Enable Breakpoint", 0, KDB_REPEAT_NONE); + kdb_register_repeat("bd", kdb_bc, "", "Disable Breakpoint", 0, KDB_REPEAT_NONE); + + kdb_register_repeat("ss", kdb_ss, "", "Single Step", 1, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("ssb", kdb_ss, "", "Single step to branch/call", 0, KDB_REPEAT_NO_ARGS); + /* + * Architecture dependent initialization. + */ + kdba_initbp(); +} --- /dev/null +++ b/kdb/kdb_bt.c @@ -0,0 +1,180 @@ +/* + * Kernel Debugger Architecture Independent Stack Traceback + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * kdb_bt + * + * This function implements the 'bt' command. Print a stack + * traceback. + * + * bt [] (addr-exp is for alternate stacks) + * btp Kernel stack for + * btt Kernel stack for task structure at + * bta [DRSTCZEUIMA] All useful processes, optionally filtered by state + * btc [] The current process on one cpu, default is all cpus + * + * bt refers to a address on the stack, that location + * is assumed to contain a return address. + * + * btt refers to the address of a struct task. + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Backtrack works best when the code uses frame pointers. But even + * without frame pointers we should get a reasonable trace. + * + * mds comes in handy when examining the stack to do a manual traceback or + * to get a starting point for bt . + */ + +static int +kdb_bt1(const struct task_struct *p, unsigned long mask, int argcount, int btaprompt) +{ + int diag; + char buffer[2]; + if (kdb_getarea(buffer[0], (unsigned long)p) || + kdb_getarea(buffer[0], (unsigned long)(p+1)-1)) + return KDB_BADADDR; + if (!kdb_task_state(p, mask)) + return 0; + kdb_printf("Stack traceback for pid %d\n", p->pid); + kdb_ps1(p); + diag = kdba_bt_process(p, argcount); + if (btaprompt) { + kdb_getstr(buffer, sizeof(buffer), "Enter to end, to continue:"); + if (buffer[0] == 'q') { + kdb_printf("\n"); + return 1; + } + } + touch_nmi_watchdog(); + return 0; +} + +int +kdb_bt(int argc, const char **argv) +{ + int diag; + int argcount = 5; + int btaprompt = 1; + int nextarg; + unsigned long addr; + long offset; + + kdbgetintenv("BTARGS", &argcount); /* Arguments to print */ + kdbgetintenv("BTAPROMPT", &btaprompt); /* Prompt after each proc in bta */ + + if (strcmp(argv[0], "bta") == 0) { + struct task_struct *g, *p; + unsigned long cpu; + unsigned long mask = kdb_task_state_string(argc ? argv[1] : NULL); + if (argc == 0) + kdb_ps_suppressed(); + /* Run the active tasks first */ + for (cpu = 0; cpu < NR_CPUS; ++cpu) { + if (!cpu_online(cpu)) + continue; + p = kdb_curr_task(cpu); + if (kdb_bt1(p, mask, argcount, btaprompt)) + return 0; + } + /* Now the inactive tasks */ + kdb_do_each_thread(g, p) { + if (task_curr(p)) + continue; + if (kdb_bt1(p, mask, argcount, btaprompt)) + return 0; + } kdb_while_each_thread(g, p); + } else if (strcmp(argv[0], "btp") == 0) { + struct task_struct *p; + unsigned long pid; + if (argc != 1) + return KDB_ARGCOUNT; + if ((diag = kdbgetularg((char *)argv[1], &pid))) + return diag; + if ((p = find_task_by_pid_ns(pid, &init_pid_ns))) { + kdba_set_current_task(p); + return kdb_bt1(p, ~0UL, argcount, 0); + } + kdb_printf("No process with pid == %ld found\n", pid); + return 0; + } else if (strcmp(argv[0], "btt") == 0) { + if (argc != 1) + return KDB_ARGCOUNT; + if ((diag = kdbgetularg((char *)argv[1], &addr))) + return diag; + kdba_set_current_task((struct task_struct *)addr); + return kdb_bt1((struct task_struct *)addr, ~0UL, argcount, 0); + } else if (strcmp(argv[0], "btc") == 0) { + unsigned long cpu = ~0; + struct kdb_running_process *krp; + const struct task_struct *save_current_task = kdb_current_task; + char buf[80]; + if (argc > 1) + return KDB_ARGCOUNT; + if (argc == 1 && (diag = kdbgetularg((char *)argv[1], &cpu))) + return diag; + /* Recursive use of kdb_parse, do not use argv after this point */ + argv = NULL; + if (cpu != ~0) { + krp = kdb_running_process + cpu; + if (cpu >= NR_CPUS || !krp->seqno || !cpu_online(cpu)) { + kdb_printf("no process for cpu %ld\n", cpu); + return 0; + } + sprintf(buf, "btt 0x%p\n", krp->p); + kdb_parse(buf); + return 0; + } + kdb_printf("btc: cpu status: "); + kdb_parse("cpu\n"); + for (cpu = 0, krp = kdb_running_process; cpu < NR_CPUS; ++cpu, ++krp) { + if (!cpu_online(cpu) || !krp->seqno) + continue; + sprintf(buf, "btt 0x%p\n", krp->p); + kdb_parse(buf); + touch_nmi_watchdog(); + } + kdba_set_current_task(save_current_task); + return 0; + } else { + if (argc) { + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, + &offset, NULL); + if (diag) + return diag; + return kdba_bt_address(addr, argcount); + } else { + return kdb_bt1(kdb_current_task, ~0UL, argcount, 0); + } + } + + /* NOTREACHED */ + return 0; +} --- /dev/null +++ b/kdb/kdb_cmds @@ -0,0 +1,33 @@ +# Initial commands for kdb, alter to suit your needs. +# These commands are executed in kdb_init() context, no SMP, no +# processes. Commands that require process data (including stack or +# registers) are not reliable this early. set and bp commands should +# be safe. Global breakpoint commands affect each cpu as it is booted. + +# Standard debugging information for first level support, just type archkdb +# or archkdbcpu or archkdbshort at the kdb prompt. + +defcmd archkdb "" "First line arch debugging" + set BTSYMARG 1 + set BTARGS 9 + pid R + -archkdbcommon + r + -bta +endefcmd + +defcmd archkdbcpu "" "archkdb with only tasks on cpus" + set BTSYMARG 1 + set BTARGS 9 + pid R + -archkdbcommon + -btc +endefcmd + +defcmd archkdbshort "" "archkdb with less detailed backtrace" + set BTSYMARG 0 + set BTARGS 0 + pid R + -archkdbcommon + -bta +endefcmd --- /dev/null +++ b/kdb/kdb_id.c @@ -0,0 +1,236 @@ +/* + * Kernel Debugger Architecture Independent Instruction Disassembly + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include + +disassemble_info kdb_di; + +/* + * kdb_id + * + * Handle the id (instruction display) command. + * + * id [] + * + * Parameters: + * argc Count of arguments in argv + * argv Space delimited command line arguments + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic if failure. + * Locking: + * None. + * Remarks: + */ + +int +kdb_id(int argc, const char **argv) +{ + kdb_machreg_t pc; + int icount; + int diag; + int i; + char *mode; + int nextarg; + long offset = 0; + static kdb_machreg_t lastpc; + struct disassemble_info *dip = &kdb_di; + char lastbuf[50]; + unsigned long word; + + kdb_di.fprintf_func = kdb_dis_fprintf; + kdba_id_init(&kdb_di); + + if (argc != 1) { + if (lastpc == 0) { + return KDB_ARGCOUNT; + } else { + 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); + if (diag) + return diag; + kdba_check_pc(&pc); + if (kdb_getarea(word, pc)) + return(0); + + /* + * Number of lines to display + */ + diag = kdbgetintenv("IDCOUNT", &icount); + if (diag) + return diag; + + mode = kdbgetenv("IDMODE"); + diag = kdba_id_parsemode(mode, dip); + if (diag) { + return diag; + } + + for(i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static struct console *kdbcons; + +#ifdef CONFIG_PPC64 +#include +#endif + +#define CMD_BUFLEN 256 +char kdb_prompt_str[CMD_BUFLEN]; + +extern int kdb_grepping_flag; +extern char kdb_grep_string[]; +extern int kdb_grep_leading; +extern int kdb_grep_trailing; + +/* + * kdb_read + * + * This function reads a string of characters, terminated by + * a newline, or by reaching the end of the supplied buffer, + * from the current kernel debugger console device. + * Parameters: + * buffer - Address of character buffer to receive input characters. + * bufsize - size, in bytes, of the character buffer + * Returns: + * Returns a pointer to the buffer containing the received + * character string. This string will be terminated by a + * newline character. + * Locking: + * No locks are required to be held upon entry to this + * function. It is not reentrant - it relies on the fact + * that while kdb is running on any one processor all other + * processors will be spinning at the kdb barrier. + * Remarks: + * + * Davidm asks, why doesn't kdb use the console abstraction; + * here are some reasons: + * - you cannot debug the console abstraction with kdb if + * kdb uses it. + * - you rely on the correct functioning of the abstraction + * in the presence of general system failures. + * - You must acquire the console spinlock thus restricting + * the usability - what if the kernel fails with the spinlock + * held - one still wishes to debug such situations. + * - How about debugging before the console(s) are registered? + * - None of the current consoles (sercons, vt_console_driver) + * have read functions defined. + * - The standard pc keyboard and terminal drivers are interrupt + * driven. We cannot enable interrupts while kdb is active, + * so the standard input functions cannot be used by kdb. + * + * An implementation could be improved by removing the need for + * lock acquisition - just keep a 'struct console *kdbconsole;' global + * variable which refers to the preferred kdb console. + * + * The bulk of this function is architecture dependent. + * + * The buffer size must be >= 2. A buffer size of 2 means that the caller only + * wants a single key. + * + * An escape key could be the start of a vt100 control sequence such as \e[D + * (left arrow) or it could be a character in its own right. The standard + * method for detecting the difference is to wait for 2 seconds to see if there + * are any other characters. kdb is complicated by the lack of a timer service + * (interrupts are off), by multiple input sources and by the need to sometimes + * return after just one key. Escape sequence processing has to be done as + * states in the polling loop. + */ + +char * +kdb_read(char *buffer, size_t bufsize) +{ + char *cp = buffer; + char *bufend = buffer+bufsize-2; /* Reserve space for newline and null byte */ + + char *lastchar; + char *p_tmp; + char tmp; + static char tmpbuffer[CMD_BUFLEN]; + int len = strlen(buffer); + int len_tmp; + int tab=0; + int count; + int i; + int diag, dtab_count; + +#define ESCAPE_UDELAY 1000 +#define ESCAPE_DELAY 2*1000000/ESCAPE_UDELAY /* 2 seconds worth of udelays */ + char escape_data[5]; /* longest vt100 escape sequence is 4 bytes */ + char *ped = escape_data; + int escape_delay = 0; + get_char_func *f, *f_escape = NULL; + + diag = kdbgetintenv("DTABCOUNT",&dtab_count); + if (diag) + dtab_count = 30; + + if (len > 0 ) { + cp += len; + if (*(buffer+len-1) == '\n') + cp--; + } + + lastchar = cp; + *cp = '\0'; + kdb_printf("%s", buffer); + + for (;;) { + int key; + for (f = &poll_funcs[0]; ; ++f) { + if (*f == NULL) { + /* Reset NMI watchdog once per poll loop */ + touch_nmi_watchdog(); + f = &poll_funcs[0]; + } + if (escape_delay == 2) { + *ped = '\0'; + ped = escape_data; + --escape_delay; + } + if (escape_delay == 1) { + key = *ped++; + if (!*ped) + --escape_delay; + break; + } + key = (*f)(); + if (key == -1) { + if (escape_delay) { + udelay(ESCAPE_UDELAY); + --escape_delay; + } + continue; + } + if (bufsize <= 2) { + if (key == '\r') + key = '\n'; + kdb_printf("%c", key); + *buffer++ = key; + *buffer = '\0'; + return buffer; + } + if (escape_delay == 0 && key == '\e') { + escape_delay = ESCAPE_DELAY; + ped = escape_data; + f_escape = f; + } + if (escape_delay) { + *ped++ = key; + if (f_escape != f) { + escape_delay = 2; + continue; + } + if (ped - escape_data == 1) { + /* \e */ + continue; + } + else if (ped - escape_data == 2) { + /* \e */ + if (key != '[') + escape_delay = 2; + continue; + } else if (ped - escape_data == 3) { + /* \e[ */ + int mapkey = 0; + switch (key) { + case 'A': mapkey = 16; break; /* \e[A, up arrow */ + case 'B': mapkey = 14; break; /* \e[B, down arrow */ + case 'C': mapkey = 6; break; /* \e[C, right arrow */ + case 'D': mapkey = 2; break; /* \e[D, left arrow */ + case '1': /* dropthrough */ + case '3': /* dropthrough */ + case '4': mapkey = -1; break; /* \e[<1,3,4>], may be home, del, end */ + } + if (mapkey != -1) { + if (mapkey > 0) { + escape_data[0] = mapkey; + escape_data[1] = '\0'; + } + escape_delay = 2; + } + continue; + } else if (ped - escape_data == 4) { + /* \e[<1,3,4> */ + int mapkey = 0; + if (key == '~') { + switch (escape_data[2]) { + case '1': mapkey = 1; break; /* \e[1~, home */ + case '3': mapkey = 4; break; /* \e[3~, del */ + case '4': mapkey = 5; break; /* \e[4~, end */ + } + } + if (mapkey > 0) { + escape_data[0] = mapkey; + escape_data[1] = '\0'; + } + escape_delay = 2; + continue; + } + } + break; /* A key to process */ + } + + if (key != 9) + tab = 0; + switch (key) { + case 8: /* backspace */ + if (cp > buffer) { + if (cp < lastchar) { + memcpy(tmpbuffer, cp, lastchar - cp); + memcpy(cp-1, tmpbuffer, lastchar - cp); + } + *(--lastchar) = '\0'; + --cp; + kdb_printf("\b%s \r", cp); + tmp = *cp; + *cp = '\0'; + kdb_printf(kdb_prompt_str); + kdb_printf("%s", buffer); + *cp = tmp; + } + break; + case 13: /* enter \r */ + case 10: /* enter \n */ + *lastchar++ = '\n'; + *lastchar++ = '\0'; + kdb_printf("\n"); + return buffer; + case 4: /* Del */ + if(cp < lastchar) { + memcpy(tmpbuffer, cp+1, lastchar - cp -1); + memcpy(cp, tmpbuffer, lastchar - cp -1); + *(--lastchar) = '\0'; + kdb_printf("%s \r", cp); + tmp = *cp; + *cp = '\0'; + kdb_printf(kdb_prompt_str); + kdb_printf("%s", buffer); + *cp = tmp; + } + break; + case 1: /* Home */ + if(cp > buffer) { + kdb_printf("\r"); + kdb_printf(kdb_prompt_str); + cp = buffer; + } + break; + case 5: /* End */ + if(cp < lastchar) { + kdb_printf("%s", cp); + cp = lastchar; + } + break; + case 2: /* Left */ + if (cp > buffer) { + kdb_printf("\b"); + --cp; + } + break; + case 14: /* Down */ + memset(tmpbuffer, ' ', strlen(kdb_prompt_str)+(lastchar-buffer)); + *(tmpbuffer+strlen(kdb_prompt_str)+(lastchar-buffer)) = '\0'; + kdb_printf("\r%s\r", tmpbuffer); + *lastchar = (char)key; + *(lastchar+1) = '\0'; + return lastchar; + case 6: /* Right */ + if (cp < lastchar) { + kdb_printf("%c", *cp); + ++cp; + } + break; + case 16: /* Up */ + memset(tmpbuffer, ' ', strlen(kdb_prompt_str)+(lastchar-buffer)); + *(tmpbuffer+strlen(kdb_prompt_str)+(lastchar-buffer)) = '\0'; + kdb_printf("\r%s\r", tmpbuffer); + *lastchar = (char)key; + *(lastchar+1) = '\0'; + return lastchar; + case 9: /* Tab */ + if (tab < 2) + ++tab; + p_tmp = buffer; + while(*p_tmp==' ') p_tmp++; + if (p_tmp<=cp) { + memcpy(tmpbuffer, p_tmp, cp-p_tmp); + *(tmpbuffer + (cp-p_tmp)) = '\0'; + p_tmp = strrchr(tmpbuffer, ' '); + if (p_tmp) + ++p_tmp; + else + p_tmp = tmpbuffer; + len = strlen(p_tmp); + count = kallsyms_symbol_complete(p_tmp, sizeof(tmpbuffer) - (p_tmp - tmpbuffer)); + if (tab == 2) { + if (count > 0) { + kdb_printf("\n%d symbols are found.", count); + if(count>dtab_count) { + count=dtab_count; + kdb_printf(" But only first %d symbols will be printed.\nYou can change the environment variable DTABCOUNT.", count); + } + kdb_printf("\n"); + for(i=0;i=dtab_count)kdb_printf("..."); + kdb_printf("\n"); + kdb_printf(kdb_prompt_str); + kdb_printf("%s", buffer); + } + } + else { + if (count > 0) { + len_tmp = strlen(p_tmp); + strncpy(p_tmp+len_tmp,cp, lastchar-cp+1); + len_tmp = strlen(p_tmp); + strncpy(cp, p_tmp+len, len_tmp-len+1); + len = len_tmp - len; + kdb_printf("%s", cp); + cp+=len; + lastchar+=len; + } + } + kdb_nextline = 1; /* reset output line number */ + } + break; + default: + if (key >= 32 &&lastchar < bufend) { + if (cp < lastchar) { + memcpy(tmpbuffer, cp, lastchar - cp); + memcpy(cp+1, tmpbuffer, lastchar - cp); + *++lastchar = '\0'; + *cp = key; + kdb_printf("%s\r", cp); + ++cp; + tmp = *cp; + *cp = '\0'; + kdb_printf(kdb_prompt_str); + kdb_printf("%s", buffer); + *cp = tmp; + } else { + *++lastchar = '\0'; + *cp++ = key; + kdb_printf("%c", key); + } + } + break; + } + } +} + +/* + * kdb_getstr + * + * Print the prompt string and read a command from the + * input device. + * + * Parameters: + * buffer Address of buffer to receive command + * bufsize Size of buffer in bytes + * prompt Pointer to string to use as prompt string + * Returns: + * Pointer to command buffer. + * Locking: + * None. + * Remarks: + * For SMP kernels, the processor number will be + * substituted for %d, %x or %o in the prompt. + */ + +char * +kdb_getstr(char *buffer, size_t bufsize, char *prompt) +{ + if(prompt && kdb_prompt_str!=prompt) + strncpy(kdb_prompt_str, prompt, CMD_BUFLEN); + kdb_printf(kdb_prompt_str); + kdb_nextline = 1; /* Prompt and input resets line number */ + return kdb_read(buffer, bufsize); +} + +/* + * kdb_input_flush + * + * Get rid of any buffered console input. + * + * Parameters: + * none + * Returns: + * nothing + * Locking: + * none + * Remarks: + * Call this function whenever you want to flush input. If there is any + * outstanding input, it ignores all characters until there has been no + * data for approximately half a second. + */ + +#define FLUSH_UDELAY 100 +#define FLUSH_DELAY 500000/FLUSH_UDELAY /* 0.5 seconds worth of udelays */ + +static void +kdb_input_flush(void) +{ + get_char_func *f; + int flush_delay = 1; + while (flush_delay--) { + touch_nmi_watchdog(); + for (f = &poll_funcs[0]; *f; ++f) { + if ((*f)() != -1) { + flush_delay = FLUSH_DELAY; + break; + } + } + if (flush_delay) + udelay(FLUSH_UDELAY); + } +} + +/* + * kdb_printf + * + * Print a string to the output device(s). + * + * Parameters: + * printf-like format and optional args. + * Returns: + * 0 + * Locking: + * None. + * Remarks: + * use 'kdbcons->write()' to avoid polluting 'log_buf' with + * kdb output. + * + * If the user is doing a cmd args | grep srch + * then kdb_grepping_flag is set. + * In that case we need to accumulate full lines (ending in \n) before + * searching for the pattern. + */ + +static char kdb_buffer[256]; /* A bit too big to go on stack */ +static char *next_avail=kdb_buffer; +static int size_avail; +static int suspend_grep=0; + +/* + * search arg1 to see if it contains arg2 + * (kdmain.c provides flags for ^pat and pat$) + * + * return 1 for found, 0 for not found + */ +int +kdb_search_string(char *searched, char *searchfor) +{ + char firstchar, *cp; + int len1, len2; + + /* not counting the newline at the end of "searched" */ + len1 = strlen(searched)-1; + len2 = strlen(searchfor); + if (len1 < len2) return 0; + if (kdb_grep_leading && kdb_grep_trailing && len1 != len2) return 0; + + if (kdb_grep_leading) { + if (!strncmp(searched, searchfor, len2)) { + return 1; + } + } else if (kdb_grep_trailing) { + if (!strncmp(searched+len1-len2, searchfor, len2)) { + return 1; + } + } else { + firstchar = *searchfor; + cp = searched; + while ((cp = strchr(cp,firstchar))) { + if (!strncmp(cp, searchfor, len2)) { + return 1; + } + cp++; + } + } + return 0; +} + +void +kdb_printf(const char *fmt, ...) +{ + va_list ap; + int diag; + int linecount; + int logging, saved_loglevel = 0; + int do_longjmp = 0; + int got_printf_lock = 0; + int fnd, len; + char *cp, *cp2, *cphold = NULL, replaced_byte = ' '; + char *moreprompt = "more> "; + struct console *c = console_drivers; + static DEFINE_SPINLOCK(kdb_printf_lock); + unsigned long uninitialized_var(flags); + + preempt_disable(); + /* Serialize kdb_printf if multiple cpus try to write at once. + * But if any cpu goes recursive in kdb, just print the output, + * even if it is interleaved with any other text. + */ + if (!KDB_STATE(PRINTF_LOCK)) { + KDB_STATE_SET(PRINTF_LOCK); + spin_lock_irqsave(&kdb_printf_lock, flags); + got_printf_lock = 1; + atomic_inc(&kdb_event); + } else { + __acquire(kdb_printf_lock); + } + atomic_inc(&kdb_8250); + + diag = kdbgetintenv("LINES", &linecount); + if (diag || linecount <= 1) + linecount = 22; + + diag = kdbgetintenv("LOGGING", &logging); + if (diag) + logging = 0; + + if (!kdb_grepping_flag || suspend_grep) { + /* normally, every vsnprintf starts a new buffer */ + next_avail = kdb_buffer; + size_avail = sizeof(kdb_buffer); + } + va_start(ap, fmt); + vsnprintf(next_avail, size_avail, fmt, ap); + va_end(ap); + + /* + * If kdb_parse() found that the command was cmd xxx | grep yyy + * then kdb_grepping_flag is set, and kdb_grep_string contains yyy + * + * Accumulate the print data up to a newline before searching it. + * (vsnprintf does null-terminate the string that it generates) + */ + + /* skip the search if prints are temporarily unconditional */ + if (! suspend_grep) { + + if (kdb_grepping_flag) { + cp = strchr(kdb_buffer, '\n'); + if (!cp) { + /* + * Special cases that don't end with newlines + * but should be written without one: + * The "[nn]kdb> " prompt should + * appear at the front of the buffer. + * + * The "[nn]more " prompt should also be + * (MOREPROMPT -> moreprompt) + * written * but we print that ourselves, + * we set the suspend_grep flag to make + * it unconditional. + * + */ + if (next_avail == kdb_buffer) { + /* + * these should occur after a newline, + * so they will be at the front of + * the buffer + */ + cp2 = kdb_buffer; + len = strlen(kdb_prompt_str); + if (!strncmp(cp2,kdb_prompt_str, len)) { + /* + * We're about to start a new + * command, so we can go back + * to normal mode. + */ + kdb_grepping_flag = 0; + goto kdb_printit; + } + } + /* no newline; don't search/write the buffer + until one is there */ + len = strlen(kdb_buffer); + next_avail = kdb_buffer + len; + size_avail = sizeof(kdb_buffer) - len; + goto kdb_print_out; + } + + /* + * The newline is present; print through it or discard + * it, depending on the results of the search. + */ + cp++; /* to byte after the newline */ + replaced_byte = *cp; /* remember what/where it was */ + cphold = cp; + *cp = '\0'; /* end the string for our search */ + + /* + * We now have a newline at the end of the string + * Only continue with this output if it contains the + * search string. + */ + fnd = kdb_search_string(kdb_buffer, kdb_grep_string); + if (!fnd) { + /* + * At this point the complete line at the start + * of kdb_buffer can be discarded, as it does + * not contain what the user is looking for. + * Shift the buffer left. + */ + *cphold = replaced_byte; + strcpy(kdb_buffer, cphold); + len = strlen(kdb_buffer); + next_avail = kdb_buffer + len; + size_avail = sizeof(kdb_buffer) - len; + goto kdb_print_out; + } + /* + * at this point the string is a full line and + * should be printed, up to the null. + */ + } + } +kdb_printit: + + /* + * Write to all consoles. + */ +#ifdef CONFIG_SPARC64 + if (c == NULL) + prom_printf("%s", kdb_buffer); + else +#endif + +#ifdef CONFIG_PPC64 + if (udbg_write) + udbg_write(kdb_buffer, strlen(kdb_buffer)); + else +#endif + + while (c) { + c->write(c, kdb_buffer, strlen(kdb_buffer)); + touch_nmi_watchdog(); + c = c->next; + } + if (logging) { + saved_loglevel = console_loglevel; + console_loglevel = 0; + printk("%s", kdb_buffer); + } + + if (KDB_STATE(LONGJMP) && strchr(kdb_buffer, '\n')) + kdb_nextline++; + + /* check for having reached the LINES number of printed lines */ + if (kdb_nextline == linecount) { + char buf1[16]=""; +#if defined(CONFIG_SMP) + char buf2[32]; +#endif + + /* Watch out for recursion here. Any routine that calls + * kdb_printf will come back through here. And kdb_read + * uses kdb_printf to echo on serial consoles ... + */ + kdb_nextline = 1; /* In case of recursion */ + + /* + * Pause until cr. + */ + moreprompt = kdbgetenv("MOREPROMPT"); + if (moreprompt == NULL) { + moreprompt = "more> "; + } + +#if defined(CONFIG_SMP) + if (strchr(moreprompt, '%')) { + sprintf(buf2, moreprompt, get_cpu()); + put_cpu(); + moreprompt = buf2; + } +#endif + + kdb_input_flush(); + c = console_drivers; +#ifdef CONFIG_SPARC64 + if (c == NULL) + prom_printf("%s", moreprompt); + else +#endif + +#ifdef CONFIG_PPC64 + if (udbg_write) + udbg_write(moreprompt, strlen(moreprompt)); + else +#endif + + while (c) { + c->write(c, moreprompt, strlen(moreprompt)); + touch_nmi_watchdog(); + c = c->next; + } + + if (logging) + printk("%s", moreprompt); + + kdb_read(buf1, 2); /* '2' indicates to return immediately after getting one key. */ + kdb_nextline = 1; /* Really set output line 1 */ + + /* empty and reset the buffer: */ + kdb_buffer[0] = '\0'; + next_avail = kdb_buffer; + size_avail = sizeof(kdb_buffer); + if ((buf1[0] == 'q') || (buf1[0] == 'Q')) { + /* user hit q or Q */ + do_longjmp = 1; + KDB_FLAG_SET(CMD_INTERRUPT); /* command was interrupted */ + /* end of command output; back to normal mode */ + kdb_grepping_flag = 0; + kdb_printf("\n"); + } else if (buf1[0] && buf1[0] != '\n') { + /* user hit something other than enter */ + suspend_grep = 1; /* for this recursion */ + kdb_printf("\nOnly 'q' or 'Q' are processed at more prompt, input ignored\n"); + } else if (kdb_grepping_flag) { + /* user hit enter */ + suspend_grep = 1; /* for this recursion */ + kdb_printf("\n"); + } + kdb_input_flush(); + } + + /* + * For grep searches, shift the printed string left. + * replaced_byte contains the character that was overwritten with + * the terminating null, and cphold points to the null. + * Then adjust the notion of available space in the buffer. + */ + if (kdb_grepping_flag && !suspend_grep) { + *cphold = replaced_byte; + strcpy(kdb_buffer, cphold); + len = strlen(kdb_buffer); + next_avail = kdb_buffer + len; + size_avail = sizeof(kdb_buffer) - len; + } + +kdb_print_out: + suspend_grep = 0; /* end of what may have been a recursive call */ + if (logging) { + console_loglevel = saved_loglevel; + } + atomic_dec(&kdb_8250); + if (KDB_STATE(PRINTF_LOCK) && got_printf_lock) { + got_printf_lock = 0; + spin_unlock_irqrestore(&kdb_printf_lock, flags); + KDB_STATE_CLEAR(PRINTF_LOCK); + atomic_dec(&kdb_event); + } else { + __release(kdb_printf_lock); + } + preempt_enable(); + if (do_longjmp) +#ifdef kdba_setjmp + kdba_longjmp(&kdbjmpbuf[smp_processor_id()], 1) +#endif /* kdba_setjmp */ + ; +} + +/* + * kdb_io_init + * + * Initialize kernel debugger output environment. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * Select a console device. Only use a VT console if the user specified + * or defaulted console= /^tty[0-9]*$/ + */ + +void __init +kdb_io_init(void) +{ + /* + * Select a console. + */ + struct console *c = console_drivers; + int vt_console = 0; + + while (c) { + if ((c->flags & CON_CONSDEV) && !kdbcons) + kdbcons = c; + if ((c->flags & CON_ENABLED) && + strncmp(c->name, "tty", 3) == 0) { + char *p = c->name + 3; + while (isdigit(*p)) + ++p; + if (*p == '\0') + vt_console = 1; + } + c = c->next; + } + + if (kdbcons == NULL) { + printk(KERN_ERR "kdb: Initialization failed - no console. kdb is disabled.\n"); + KDB_FLAG_SET(NO_CONSOLE); + kdb_on = 0; + } + if (!vt_console) + KDB_FLAG_SET(NO_VT_CONSOLE); + kdb_input_flush(); + return; +} + +#ifdef CONFIG_KDB_USB + +int kdb_no_usb = 0; + +static int __init opt_kdbnousb(char *str) +{ + kdb_no_usb = 1; + return 0; +} + +early_param("kdbnousb", opt_kdbnousb); + +#endif + +EXPORT_SYMBOL(kdb_read); --- /dev/null +++ b/kdb/kdbdereference.c @@ -0,0 +1,7258 @@ +/* + * + * Most of this code is borrowed and adapted from the lkcd command "lcrash" + * and its supporting libarary. + * + * This kdb commands for casting memory structures. + * It provides + * "print" "px", "pd" * + * + * Careful of porting the klib KL_XXX functions (they call thru a jump table + * that we don't use here) + * + * The kernel type information is added be insmod'g the kdb debuginfo module + * It loads symbolic debugging info (provided from lcrash -o), + * (this information originally comes from the lcrash "kerntypes" file) + * + */ + +#define VMALLOC_START_IA64 0xa000000200000000 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "modules/lcrash/klib.h" +#include "modules/lcrash/kl_stringtab.h" +#include "modules/lcrash/kl_btnode.h" +#include "modules/lcrash/lc_eval.h" + +#undef next_node /* collision with nodemask.h */ +int have_debug_file = 0; +dbg_sym_t *types_tree_head; +dbg_sym_t *typedefs_tree_head; +kltype_t *kltype_array; +dbg_sym_t *dsym_types_array; + + +EXPORT_SYMBOL(types_tree_head); +EXPORT_SYMBOL(typedefs_tree_head); +EXPORT_SYMBOL(kltype_array); +EXPORT_SYMBOL(dsym_types_array); + +#define C_HEX 0x0002 +#define C_WHATIS 0x0004 +#define C_NOVARS 0x0008 +#define C_SIZEOF 0x0010 +#define C_SHOWOFFSET 0x0020 +#define C_LISTHEAD 0x0040 +#define C_LISTHEAD_N 0x0080 /* walk using list_head.next */ +#define C_LISTHEAD_P 0x0100 /* walk using list_head.prev */ +#define C_BINARY 0x0200 +#define MAX_LONG_LONG 0xffffffffffffffffULL +klib_t kdb_klib; +klib_t *KLP = &kdb_klib; +k_error_t klib_error = 0; +dbg_sym_t *type_tree = (dbg_sym_t *)NULL; +dbg_sym_t *typedef_tree = (dbg_sym_t *)NULL; +dbg_sym_t *func_tree = (dbg_sym_t *)NULL; +dbg_sym_t *srcfile_tree = (dbg_sym_t *)NULL; +dbg_sym_t *var_tree = (dbg_sym_t *)NULL; +dbg_sym_t *xtype_tree = (dbg_sym_t *)NULL; +dbg_hashrec_t *dbg_hash[TYPE_NUM_SLOTS]; +int all_count, deall_count; +void single_type(char *str); +void sizeof_type(char *str); +typedef struct chunk_s { + struct chunk_s *next; /* Must be first */ + struct chunk_s *prev; /* Must be second */ + void *addr; + struct bucket_s *bucketp; + uint32_t chunksz; /* size of memory chunk (via malloc()) */ + uint32_t blksz; /* Not including header */ + short blkcount; /* Number of blksz blocks in chunk */ +} chunk_t; + +typedef struct blkhdr_s { + struct blkhdr_s *next; + union { + struct blkhdr_s *prev; + chunk_t *chunkp; + } b_un; + int flg; + int size; +} blkhdr_t; + +int ptrsz64 = ((int)sizeof(void *) == 8); +alloc_functions_t alloc_functions; + +/* + * return 1 if addr is invalid + */ +static int +invalid_address(kaddr_t addr, int count) +{ + unsigned char c; + unsigned long lcount; + /* FIXME: untested? */ + lcount = count; + /* FIXME: use kdb_verify_area */ + while (count--) { + if (kdb_getarea(c, addr)) + return 1; + } + return 0; +} + +/* + * wrappers for calls to kernel-style allocation/deallocation + */ +static void * +kl_alloc_block(int size) +{ + void *vp; + + vp = kmalloc(size, GFP_KERNEL); + if (!vp) { + kdb_printf ("kmalloc of %d bytes failed\n", size); + } + /* important: the lcrash code sometimes assumes that the + * allocation is zeroed out + */ + memset(vp, 0, size); + all_count++; + return vp; +} +static void +kl_free_block(void *vp) +{ + kfree(vp); + deall_count++; + return; +} + +int +get_value(char *s, uint64_t *value) +{ + return kl_get_value(s, NULL, 0, value); +} + +/* + * kl_get_block() + * + * Read a size block from virtual address addr in the system memory image. + */ +k_error_t +kl_get_block(kaddr_t addr, unsigned size, void *bp, void *mmap) +{ + if (!bp) { + return(KLE_NULL_BUFF); + } else if (!size) { + return(KLE_ZERO_SIZE); + } + + memcpy(bp, (void *)addr, size); + + return(0); +} + +/* + * print_value() + */ +void +print_value(char *ldstr, uint64_t value, int width) +{ + int w = 0; + char fmtstr[12], f, s[2]="\000\000"; + + if (ldstr) { + kdb_printf("%s", ldstr); + } + s[0] = '#'; + f = 'x'; + if (width) { + if (ptrsz64) { + w = 18; /* due to leading "0x" */ + } else { + w = 10; /* due to leading "0x" */ + } + } + if (w) { + sprintf(fmtstr, "%%%s%d"FMT64"%c", s, w, f); + } else { + sprintf(fmtstr, "%%%s"FMT64"%c", s, f); + } + kdb_printf(fmtstr, value); +} + +/* + * print_list_head() + */ +void +print_list_head(kaddr_t saddr) +{ + print_value("STRUCT ADDR: ", (uint64_t)saddr, 8); + kdb_printf("\n"); +} + +/* + * check_prev_ptr() + */ +void +check_prev_ptr(kaddr_t ptr, kaddr_t prev) +{ + if(ptr != prev) { + kdb_printf("\nWARNING: Pointer broken. %#"FMTPTR"x," + " SHOULD BE: %#"FMTPTR"x\n", prev, ptr); + } +} + +/* + * kl_kaddr() -- Return a kernel virtual address stored in a structure + * + * Pointer 'p' points to a kernel structure + * of type 's.' Get the kernel address located in member 'm.' + */ +kaddr_t +kl_kaddr(void *p, char *s, char *m) +{ + uint64_t *u64p; + int offset; + + offset = kl_member_offset(s, m); + u64p = (uint64_t *)(p + offset); + return((kaddr_t)*u64p); +} + +/* + * walk_structs() -- walk linked lists of kernel data structures + */ +int +walk_structs(char *s, char *f, char *member, kaddr_t addr, int flags) +{ + int size, offset, mem_offset=0; + kaddr_t last = 0, next; + kltype_t *klt = (kltype_t *)NULL, *memklt=(kltype_t *)NULL; + unsigned long long iter_threshold = 10000; + + int counter = 0; + kaddr_t head=0, head_next=0, head_prev=0, entry=0; + kaddr_t entry_next=0, entry_prev; + + /* field name of link pointer, determine its offset in the struct. */ + if ((offset = kl_member_offset(s, f)) == -1) { + kdb_printf("Could not determine offset for member %s of %s.\n", + f, s); + return 0; + } + + /* Get the type of the enclosing structure */ + if (!(klt = kl_find_type(s, (KLT_STRUCT|KLT_UNION)))) { + kdb_printf("Could not find the type of %s\n", s); + return(1); + } + + /* Get the struct size */ + if ((size = kl_struct_len(s)) == 0) { + kdb_printf ("could not get the length of %s\n", s); + return(1); + } + + /* test for a named member of the structure that should be displayed */ + if (member) { + memklt = kl_get_member(klt, member); + if (!memklt) { + kdb_printf ("%s has no member %s\n", s, member); + return 1; + } + mem_offset = kl_get_member_offset(klt, member); + } + + if ((next = addr)) { + /* get head of list (anchor) when struct list_head is used */ + if (flags & C_LISTHEAD) { + head = next; + if (invalid_address(head, sizeof(head))) { + kdb_printf ("invalid address %#lx\n", + head); + return 1; + } + /* get contents of addr struct member */ + head_next = kl_kaddr((void *)head, "list_head", "next"); + if (invalid_address(head, sizeof(head_next))) { + kdb_printf ("invalid address %#lx\n", + head_next); + return 1; + } + /* get prev field of anchor */ + head_prev = kl_kaddr((void *)head, "list_head", "prev"); + if (invalid_address(head, sizeof(head_prev))) { + kdb_printf ("invalid address %#lx\n", + head_prev); + return 1; + } + entry = 0; + } + } + + while(next && counter < iter_threshold) { + counter++; + if (counter > iter_threshold) { + kdb_printf("\nWARNING: Iteration threshold reached.\n"); + kdb_printf("Current threshold: %lld\n", iter_threshold); + break; + } + if(flags & C_LISTHEAD) { + if(!(entry)){ + if(flags & C_LISTHEAD_N){ + entry = head_next; + } else { + entry = head_prev; + } + last = head; + } + + if(head == entry) { + if(flags & C_LISTHEAD_N){ + check_prev_ptr(last, head_prev); + } else { + check_prev_ptr(last, head_next); + } + break; + } + + next = entry - offset; /* next structure */ + /* check that the whole structure can be addressed */ + if (invalid_address(next, size)) { + kdb_printf( + "invalid struct address %#lx\n", next); + return 1; + } + /* and validate that it points to valid addresses */ + entry_next = kl_kaddr((void *)entry,"list_head","next"); + if (invalid_address(entry_next, sizeof(entry_next))) { + kdb_printf("invalid address %#lx\n", + entry_next); + return 1; + } + entry_prev = kl_kaddr((void *)entry,"list_head","prev"); + if (invalid_address(entry_prev, sizeof(entry_prev))) { + kdb_printf("invalid address %#lx\n", + entry_prev); + return 1; + } + if(flags & C_LISTHEAD_N){ + check_prev_ptr(last, entry_prev); + } else { + check_prev_ptr(last, entry_next); + } + print_list_head(next); + last = entry; + if(flags & C_LISTHEAD_N){ + entry = entry_next; /* next list_head */ + } else { + entry = entry_prev; /* next list_head */ + } + } + + if (memklt) { + /* print named sub-structure in C-like struct format. */ + kl_print_member( + (void *)((unsigned long)next+mem_offset), + memklt, 0, C_HEX); + } else { + /* print entire structure in C-like struct format. */ + kl_print_type((void *)next, klt, 0, C_HEX); + } + + if(!(flags & C_LISTHEAD)) { + last = next; + next = (kaddr_t) (*(uint64_t*)(next + offset)); + } + } + + return(0); +} + +/* + * Implement the lcrash walk -s command + * see lcrash cmd_walk.c + */ +int +kdb_walk(int argc, const char **argv) +{ + int i, nonoptc=0, optc=0, flags=0, init_len=0; + char *cmd, *arg, *structp=NULL, *forwp=NULL, *memberp=NULL; + char *addrp=NULL; + uint64_t value; + kaddr_t start_addr; + + all_count=0; + deall_count=0; + if (!have_debug_file) { + kdb_printf("no debuginfo file\n"); + return 0; + } + /* If there is nothing to evaluate, just return */ + if (argc == 0) { + return 0; + } + cmd = (char *)*argv; /* s/b "walk" */ + if (strcmp(cmd,"walk")) { + kdb_printf("got %s, not \"walk\"\n", cmd); + return 0; + } + + for (i=1; i<=argc; i++) { + arg = (char *)*(argv+i); + if (*arg == '-') { + optc++; + if (optc > 2) { + kdb_printf("too many options\n"); + kdb_printf("see 'walkhelp'\n"); + return 0; + } + if (*(arg+1) == 's') { + continue; /* ignore -s */ + } else if (*(arg+1) == 'h') { + if ((init_len=kl_struct_len("list_head")) + == 0) { + kdb_printf( + "could not find list_head\n"); + return 0; + } + if (*(arg+2) == 'p') { + flags = C_LISTHEAD; + flags |= C_LISTHEAD_P; + } else if (*(arg+2) == 'n') { + flags = C_LISTHEAD; + flags |= C_LISTHEAD_N; + } else { + kdb_printf("invalid -h option <%s>\n", + arg); + kdb_printf("see 'walkhelp'\n"); + return 0; + } + } else { + kdb_printf("invalid option <%s>\n", arg); + kdb_printf("see 'walkhelp'\n"); + return 0; + } + } else { + nonoptc++; + if (nonoptc > 4) { + kdb_printf("too many arguments\n"); + kdb_printf("see 'walkhelp'\n"); + return 0; + } + if (nonoptc == 1) { + structp = arg; + } else if (nonoptc == 2) { + forwp = arg; + } else if (nonoptc == 3) { + addrp = arg; + } else if (nonoptc == 4) { + /* the member is optional; if we get + a fourth, the previous was the member */ + memberp = addrp; + addrp = arg; + } else { + kdb_printf("invalid argument <%s>\n", arg); + kdb_printf("see 'walkhelp'\n"); + return 0; + } + } + } + if (nonoptc < 3) { + kdb_printf("too few arguments\n"); + kdb_printf("see 'walkhelp'\n"); + return 0; + } + if (!(flags & C_LISTHEAD)) { + if ((init_len=kl_struct_len(structp)) == 0) { + kdb_printf("could not find %s\n", structp); + return 0; + } + } + + /* Get the start address of the structure */ + if (get_value(addrp, &value)) { + kdb_printf ("address %s invalid\n", addrp); + return 0; + } + start_addr = (kaddr_t)value; + if (invalid_address(start_addr, init_len)) { + kdb_printf ("address %#lx invalid\n", start_addr); + return 0; + } + + if (memberp) { + } + + if (walk_structs(structp, forwp, memberp, start_addr, flags)) { + kdb_printf ("walk_structs failed\n"); + return 0; + } + /* kdb_printf("ptc allocated:%d deallocated:%d\n", + all_count, deall_count); */ + return 0; +} + +/* + * Implement the lcrash px (print, pd) command + * see lcrash cmd_print.c + * + * px + * e.g. px *(task_struct *)
+ */ +int +kdb_debuginfo_print(int argc, const char **argv) +{ + /* argc does not count the command itself, which is argv[0] */ + char *cmd, *next, *end, *exp, *cp; + unsigned char *buf; + int i, j, iflags; + node_t *np; + uint64_t flags = 0; + + /* If there is nothing to evaluate, just return */ + if (argc == 0) { + return 0; + } + all_count=0; + deall_count=0; + + cmd = (char *)*argv; + + /* Set up the flags value. If this command was invoked via + * "pd" or "px", then make sure the appropriate flag is set. + */ + flags = 0; + if (!strcmp(cmd, "pd") || !strcmp(cmd, "print")) { + flags = 0; + } else if (!strcmp(cmd, "px")) { + flags |= C_HEX; + } else if (!strcmp(cmd, "whatis")) { + if (argc != 1) { + kdb_printf("usage: whatis \n"); + return 0; + } + cp = (char *)*(argv+1); + single_type(cp); + /* kdb_printf("allocated:%d deallocated:%d\n", + all_count, deall_count); */ + return 0; + } else if (!strcmp(cmd, "sizeof")) { + if (!have_debug_file) { + kdb_printf("no debuginfo file\n"); + return 0; + } + if (argc != 1) { + kdb_printf("usage: sizeof type\n"); + return 0; + } + cp = (char *)*(argv+1); + sizeof_type(cp); + return 0; + } else { + kdb_printf("command error: %s\n", cmd); + return 0; + } + + /* + * Count the number of bytes necessary to hold the entire expression + * string. + */ + for (i=1, j=0; i <= argc; i++) { + j += (strlen(*(argv+i)) + 1); + } + + /* + * Allocate space for the expression string and copy the individual + * arguments into it. + */ + buf = kl_alloc_block(j); + if (!buf) { + return 0; + } + + for (i=1; i <= argc; i++) { + strcat(buf, *(argv+i)); + /* put spaces between arguments */ + if (i < argc) { + strcat(buf, " "); + } + } + + /* Walk through the expression string, expression by expression. + * Note that a comma (',') is the delimiting character between + * expressions. + */ + next = buf; + while (next) { + if ((end = strchr(next, ','))) { + *end = (char)0; + } + + /* Copy the next expression to a separate expression string. + * A separate expresison string is necessary because it is + * likely to get freed up in eval() when variables get expanded. + */ + i = strlen(next)+1; + exp = (char *)kl_alloc_block(i); + if (!exp) { + return 0; + } + strcpy(exp, next); + + /* Evaluate the expression */ + np = eval(&exp, 0); + if (!np || eval_error) { + print_eval_error(cmd, exp, + (error_token ? error_token : (char*)NULL), + eval_error, CMD_NAME_FLG); + if (np) { + free_nodes(np); + } + kl_free_block(buf); + kl_free_block(exp); + free_eval_memory(); + return 0; + } + iflags = flags; + if (print_eval_results(np, iflags)) { + free_nodes(np); + kl_free_block(buf); + free_eval_memory(); + return 0; + } + kl_free_block(exp); + + if (end) { + next = end + 1; + kdb_printf(" "); + } else { + next = (char*)NULL; + kdb_printf("\n"); + } + free_nodes(np); + } + free_eval_memory(); + kl_free_block(buf); + /* kdb_printf("allocated:%d deallocated:%d\n", + all_count, deall_count); */ + return 0; +} + +/* + * Display help for the px command + */ +int +kdb_pxhelp(int argc, const char **argv) +{ + if (have_debug_file) { + kdb_printf ("Some examples of using the px command:\n"); + kdb_printf (" the whole structure:\n"); + kdb_printf (" px *(task_struct *)0xe0000...\n"); + kdb_printf (" one member:\n"); + kdb_printf (" px (*(task_struct *)0xe0000...)->comm\n"); + kdb_printf (" the address of a member\n"); + kdb_printf (" px &((task_struct *)0xe0000...)->children\n"); + kdb_printf (" a structure pointed to by a member:\n"); + kdb_printf (" px ((*(class_device *)0xe0000...)->class)->name\n"); + kdb_printf (" array element:\n"); + kdb_printf (" px (cache_sizes *)0xa0000...[0]\n"); + kdb_printf (" px (task_struct *)(0xe0000...)->cpus_allowed.bits[0]\n"); + } else { + kdb_printf ("There is no debug info file.\n"); + kdb_printf ("The px/pd/print commands can only evaluate "); + kdb_printf ("arithmetic expressions.\n"); + } + return 0; +} + +/* + * Display help for the walk command + */ +int +kdb_walkhelp(int argc, const char **argv) +{ + if (!have_debug_file) { + kdb_printf("no debuginfo file\n"); + return 0; + } + kdb_printf ("Using the walk command:\n"); + kdb_printf (" (only the -s (symbolic) form is supported, so -s is ignored)\n"); + kdb_printf ("\n"); + kdb_printf (" If the list is not linked with list_head structures:\n"); + kdb_printf (" walk [-s] struct name-of-forward-pointer address\n"); + kdb_printf (" example: walk xyz_struct next 0xe00....\n"); + kdb_printf ("\n"); + kdb_printf (" If the list is linked with list_head structures, use -hn\n"); + kdb_printf (" to walk the 'next' list, -hp for the 'prev' list\n"); + kdb_printf (" walk -h[n|p] struct name-of-forward-pointer [member-to-show] address-of-list-head\n"); + kdb_printf (" example, to show the entire task_struct:\n"); + kdb_printf (" walk -hn task_struct tasks 0xe000....\n"); + kdb_printf (" example, to show the task_struct member comm:\n"); + kdb_printf (" walk -hn task_struct tasks comm 0xe000....\n"); + kdb_printf (" (address is not the address of first member's list_head, "); + kdb_printf ("but of the anchoring list_head\n"); + return 0; +} + +/* + * dup_block() + */ +void * +dup_block(void *b, int len) +{ + void *b2; + + if ((b2 = kl_alloc_block(len))) { + memcpy(b2, b, len); /* dst, src, sz */ + } + return(b2); +} + +/* + * kl_reset_error() + */ +void +kl_reset_error(void) +{ + klib_error = 0; +} + +/* + * given a symbol name, look up its address + * + * in lcrash, this would return a pointer to the syment_t in + * a binary tree of them + * + * In this one, look up the symbol in the standard kdb way, + * which fills in the kdb_symtab_t. + * Then fill in the global syment_t "lkup_syment" -- assuming + * we'll only need one at a time! + * + * kl_lkup_symname returns the address of syment_t if the symbol is + * found, else null. + * + * Note: we allocate a syment_t the caller should kfree it + */ +syment_t * +kl_lkup_symname (char *cp) +{ + syment_t *sp; + kdb_symtab_t kdb_symtab; + + if (kdbgetsymval(cp, &kdb_symtab)) { + sp = (syment_t *)kl_alloc_block(sizeof(syment_t)); + sp->s_addr = (kaddr_t)kdb_symtab.sym_start; + KL_ERROR = 0; + return (sp); + } else { + /* returns 0 if the symbol is not found */ + KL_ERROR = KLE_INVALID_VALUE; + return ((syment_t *)0); + } +} + +/* + * kl_get_ra() + * + * This function returns its own return address. + * Usefule when trying to capture where we came from. + */ +void* +kl_get_ra(void) +{ + return (__builtin_return_address(0)); +} + +/* start kl_util.c */ +/* + * Definitions for the do_math() routine. + */ +#define M_ADD '+' +#define M_SUBTRACT '-' +#define M_MULTIPLY '*' +#define M_DIVIDE '/' + +/* + * do_math() -- Calculate some math values based on a string argument + * passed into the function. For example, if you use: + * + * 0xffffc000*2+6/5-3*19-8 + * + * And you will get the value 0xffff7fc0 back. I could + * probably optimize this a bit more, but right now, it + * works, which is good enough for me. + */ +static uint64_t +do_math(char *str) +{ + int i = 0; + char *buf, *loc; + uint64_t value1, value2; + syment_t *sp; + + buf = (char *)kl_alloc_block((strlen(str) + 1)); + sprintf(buf, "%s", str); + for (i = strlen(str); i >= 0; i--) { + if ((str[i] == M_ADD) || (str[i] == M_SUBTRACT)) { + buf[i] = '\0'; + value1 = do_math(buf); + value2 = do_math(&str[i+1]); + kl_free_block((void *)buf); + if (str[i] == M_SUBTRACT) { + return value1 - value2; + } else { + return value1 + value2; + } + } + } + + for (i = strlen(str); i >= 0; i--) { + if ((str[i] == M_MULTIPLY) || (str[i] == M_DIVIDE)) { + buf[i] = '\0'; + value1 = do_math(buf); + value2 = do_math(&str[i+1]); + kl_free_block((void *)buf); + if (str[i] == M_MULTIPLY) { + return (value1 * value2); + } else { + if (value2 == 0) { + /* handle divide by zero */ + /* XXX -- set proper error code */ + klib_error = 1; + return (0); + } else { + return (value1 / value2); + } + } + } + } + + /* + * Otherwise, just process the value, and return it. + */ + sp = kl_lkup_symname(buf); + if (KL_ERROR) { + KL_ERROR = 0; + value2 = kl_strtoull(buf, &loc, 10); + if (((!value2) && (buf[0] != '0')) || (*loc) || + (!strncmp(buf, "0x", 2)) || (!strncmp(buf, "0X", 2))) { + value1 = (kaddr_t)kl_strtoull(buf, (char**)NULL, 16); + } else { + value1 = (unsigned)kl_strtoull(buf, (char**)NULL, 10); + } + } else { + value1 = (kaddr_t)sp->s_addr; + kl_free_block((void *)sp); + } + kl_free_block((void *)buf); + return (value1); +} +/* + * kl_get_value() -- Translate numeric input strings + * + * A generic routine for translating an input string (param) in a + * number of dfferent ways. If the input string is an equation + * (contains the characters '+', '-', '/', and '*'), then perform + * the math evaluation and return one of the following modes (if + * mode is passed): + * + * 0 -- if the resulting value is <= elements, if elements (number + * of elements in a table) is passed. + * + * 1 -- if the first character in param is a pound sign ('#'). + * + * 3 -- the numeric result of an equation. + * + * If the input string is NOT an equation, mode (if passed) will be + * set in one of the following ways (depending on the contents of + * param and elements). + * + * o When the first character of param is a pound sign ('#'), mode + * is set equal to one and the trailing numeric value (assumed to + * be decimal) is returned. + * + * o When the first two characters in param are "0x" or "0X," or + * when when param contains one of the characers "abcdef," or when + * the length of the input value is eight characters. mode is set + * equal to two and the numeric value contained in param is + * translated as hexadecimal and returned. + * + * o The value contained in param is translated as decimal and mode + * is set equal to zero. The resulting value is then tested to see + * if it exceeds elements (if passed). If it does, then value is + * translated as hexadecimal and mode is set equal to two. + * + * Note that mode is only set when a pointer is passed in the mode + * paramater. Also note that when elements is set equal to zero, any + * non-hex (as determined above) value not starting with a pound sign + * will be translated as hexadecimal (mode will be set equal to two) -- + * IF the length of the string of characters is less than 16 (kaddr_t). + * + */ +int +kl_get_value(char *param, int *mode, int elements, uint64_t *value) +{ + char *loc; + uint64_t v; + + kl_reset_error(); + + /* Check to see if we are going to need to do any math + */ + if (strpbrk(param, "+-/*")) { + if (!strncmp(param, "#", 1)) { + v = do_math(¶m[1]); + if (mode) { + *mode = 1; + } + } else { + v = do_math(param); + if (mode) { + if (elements && (*value <= elements)) { + *mode = 0; + } else { + *mode = 3; + } + } + } + } else { + if (!strncmp(param, "#", 1)) { + if (!strncmp(param, "0x", 2) + || !strncmp(param, "0X", 2) + || strpbrk(param, "abcdef")) { + v = kl_strtoull(¶m[1], &loc, 16); + } else { + v = kl_strtoull(¶m[1], &loc, 10); + } + if (loc) { + KL_ERROR = KLE_INVALID_VALUE; + return (1); + } + if (mode) { + *mode = 1; + } + } else if (!strncmp(param, "0x", 2) || !strncmp(param, "0X", 2) + || strpbrk(param, "abcdef")) { + v = kl_strtoull(param, &loc, 16); + if (loc) { + KL_ERROR = KLE_INVALID_VALUE; + return (1); + } + if (mode) { + *mode = 2; /* HEX VALUE */ + } + } else if (elements || (strlen(param) < 16) || + (strlen(param) > 16)) { + v = kl_strtoull(param, &loc, 10); + if (loc) { + KL_ERROR = KLE_INVALID_VALUE; + return (1); + } + if (elements && (v >= elements)) { + v = (kaddr_t)kl_strtoull(param, + (char**)NULL, 16); + if (mode) { + *mode = 2; /* HEX VALUE */ + } + } else if (mode) { + *mode = 0; + } + } else { + v = kl_strtoull(param, &loc, 16); + if (loc) { + KL_ERROR = KLE_INVALID_VALUE; + return (1); + } + if (mode) { + *mode = 2; /* ASSUME HEX VALUE */ + } + } + } + *value = v; + return (0); +} +/* end kl_util.c */ + +/* start kl_libutil.c */ +static int +valid_digit(char c, int base) +{ + switch(base) { + case 2: + if ((c >= '0') && (c <= '1')) { + return(1); + } else { + return(0); + } + case 8: + if ((c >= '0') && (c <= '7')) { + return(1); + } else { + return(0); + } + case 10: + if ((c >= '0') && (c <= '9')) { + return(1); + } else { + return(0); + } + case 16: + if (((c >= '0') && (c <= '9')) + || ((c >= 'a') && (c <= 'f')) + || ((c >= 'A') && (c <= 'F'))) { + return(1); + } else { + return(0); + } + } + return(0); +} + +static int +digit_value(char c, int base, int *val) +{ + if (!valid_digit(c, base)) { + return(1); + } + switch (base) { + case 2: + case 8: + case 10: + *val = (int)((int)(c - 48)); + break; + case 16: + if ((c >= 'a') && (c <= 'f')) { + *val = ((int)(c - 87)); + } else if ((c >= 'A') && (c <= 'F')) { + *val = ((int)(c - 55)); + } else { + *val = ((int)(c - 48)); + } + } + return(0); +} + +uint64_t +kl_strtoull(char *str, char **loc, int base) +{ + int dval; + uint64_t i = 1, v, value = 0; + char *c, *cp = str; + + *loc = (char *)NULL; + if (base == 0) { + if (!strncmp(cp, "0x", 2) || !strncmp(cp, "0X", 2)) { + base = 16; + } else if (cp[0] == '0') { + if (cp[1] == 'b') { + base = 2; + } else { + base = 8; + } + } else if (strpbrk(cp, "abcdefABCDEF")) { + base = 16; + } else { + base = 10; + } + } + if ((base == 8) && (*cp == '0')) { + cp += 1; + } else if ((base == 2) && !strncmp(cp, "0b", 2)) { + cp += 2; + } else if ((base == 16) && + (!strncmp(cp, "0x", 2) || !strncmp(cp, "0X", 2))) { + cp += 2; + } + c = &cp[strlen(cp) - 1]; + while (c >= cp) { + + if (digit_value(*c, base, &dval)) { + if (loc) { + *loc = c; + } + return(value); + } + v = dval * i; + if ((MAX_LONG_LONG - value) < v) { + return(MAX_LONG_LONG); + } + value += v; + i *= (uint64_t)base; + c--; + } + return(value); +} +/* end kl_libutil.c */ + +/* + * dbg_hash_sym() + */ +void +dbg_hash_sym(uint64_t typenum, dbg_sym_t *stp) +{ + dbg_hashrec_t *shp, *hshp; + + if ((typenum == 0) || (!stp)) { + return; + } + shp = (dbg_hashrec_t *)kl_alloc_block(sizeof(dbg_hashrec_t)); + shp->h_typenum = typenum; + shp->h_ptr = stp; + shp->h_next = (dbg_hashrec_t *)NULL; + if ((hshp = dbg_hash[TYPE_NUM_HASH(typenum)])) { + while (hshp->h_next) { + hshp = hshp->h_next; + } + hshp->h_next = shp; + } else { + dbg_hash[TYPE_NUM_HASH(typenum)] = shp; + } +} + +/* + * dbg_find_sym() + */ +dbg_sym_t * +dbg_find_sym(char *name, int type, uint64_t typenum) +{ + dbg_sym_t *stp = (dbg_sym_t *)NULL; + + if (name && strlen(name)) { + /* Cycle through the type flags and see if any records are + * present. Note that if multiple type flags or DBG_ALL is + * passed in, only the first occurance of 'name' will be + * found and returned. If name exists in multiple trees, + * then multiple searches are necessary to find them. + */ + if (type & DBG_TYPE) { + if ((stp = (dbg_sym_t *)kl_find_btnode((btnode_t *) + type_tree, name, (int *)NULL))) { + goto found_sym; + } + } + if (type & DBG_TYPEDEF) { + if ((stp = (dbg_sym_t *)kl_find_btnode((btnode_t *) + typedef_tree, name, (int *)NULL))) { + goto found_sym; + } + } + if (!stp) { + return((dbg_sym_t*)NULL); + } + } +found_sym: + if (typenum) { + dbg_hashrec_t *hshp; + + if (stp) { + if (stp->sym_typenum == typenum) { + return(stp); + } + } else if ((hshp = dbg_hash[TYPE_NUM_HASH(typenum)])) { + while (hshp) { + if (hshp->h_typenum == typenum) { + return(hshp->h_ptr); + } + hshp = hshp->h_next; + } + } + } + return(stp); +} + +/* + * kl_find_type() -- find a KLT type by name. + */ +kltype_t * +kl_find_type(char *name, int tnum) +{ + dbg_sym_t *stp; + kltype_t *kltp = (kltype_t *)NULL; + + if (!have_debug_file) { + kdb_printf("no debuginfo file\n"); + return kltp; + } + + if (!tnum || IS_TYPE(tnum)) { + if ((stp = dbg_find_sym(name, DBG_TYPE, 0))) { + kltp = (kltype_t *)stp->sym_kltype; + if (tnum && !(kltp->kl_type & tnum)) { + /* We have found a type by this name + * but it does not have the right + * type number (e.g., we're looking + * for a struct and we don't find + * a KLT_STRUCT type by this name). + */ + return((kltype_t *)NULL); + } + } + } + if (!tnum || IS_TYPEDEF(tnum)) { + if ((stp = dbg_find_sym(name, DBG_TYPEDEF, 0))) { + kltp = (kltype_t *)stp->sym_kltype; + } + } + return(kltp); +} + +/* + * kl_first_btnode() -- non-recursive implementation. + */ +btnode_t * +kl_first_btnode(btnode_t *np) +{ + if (!np) { + return((btnode_t *)NULL); + } + + /* Walk down the left side 'til the end... + */ + while (np->bt_left) { + np = np->bt_left; + } + return(np); +} + +/* + * kl_next_btnode() -- non-recursive implementation. + */ +btnode_t * +kl_next_btnode(btnode_t *node) +{ + btnode_t *np = node, *parent; + + if (np) { + if (np->bt_right) { + return(kl_first_btnode(np->bt_right)); + } else { + parent = np->bt_parent; +next: + if (parent) { + if (parent->bt_left == np) { + return(parent); + } + np = parent; + parent = parent->bt_parent; + goto next; + } + } + } + return((btnode_t *)NULL); +} + +/* + * dbg_next_sym() + */ +dbg_sym_t * +dbg_next_sym(dbg_sym_t *stp) +{ + dbg_sym_t *next_stp; + + next_stp = (dbg_sym_t *)kl_next_btnode((btnode_t *)stp); + return(next_stp); +} + +/* + * kl_prev_btnode() -- non-recursive implementation. + */ +btnode_t * +kl_prev_btnode(btnode_t *node) +{ + btnode_t *np = node, *parent; + + if (np) { + if (np->bt_left) { + np = np->bt_left; + while (np->bt_right) { + np = np->bt_right; + } + return(np); + } + parent = np->bt_parent; +next: + if (parent) { + if (parent->bt_right == np) { + return(parent); + } + np = parent; + parent = parent->bt_parent; + goto next; + } + } + return((btnode_t *)NULL); +} + +/* + * dbg_prev_sym() + */ +dbg_sym_t * +dbg_prev_sym(dbg_sym_t *stp) +{ + dbg_sym_t *prev_stp; + + prev_stp = (dbg_sym_t *)kl_prev_btnode((btnode_t *)stp); + return(prev_stp); +} + +/* + * kl_find_next_type() -- find next KLT type + */ +kltype_t * +kl_find_next_type(kltype_t *kltp, int type) +{ + kltype_t *nkltp = NULL; + dbg_sym_t *nstp; + + if (kltp && kltp->kl_ptr) { + nstp = (dbg_sym_t *)kltp->kl_ptr; + nkltp = (kltype_t *)nstp->sym_kltype; + if (type) { + while(nkltp && !(nkltp->kl_type & type)) { + if ((nstp = dbg_next_sym(nstp))) { + nkltp = (kltype_t *)nstp->sym_kltype; + } else { + nkltp = (kltype_t *)NULL; + } + } + } + } + return(nkltp); +} + +/* + * dbg_first_sym() + */ +dbg_sym_t * +dbg_first_sym(int type) +{ + dbg_sym_t *stp = (dbg_sym_t *)NULL; + + switch(type) { + case DBG_TYPE: + stp = (dbg_sym_t *) + kl_first_btnode((btnode_t *)type_tree); + break; + case DBG_TYPEDEF: + stp = (dbg_sym_t *) + kl_first_btnode((btnode_t *)typedef_tree); + break; + } + return(stp); +} + +/* + * kl_first_type() + */ +kltype_t * +kl_first_type(int tnum) +{ + kltype_t *kltp = NULL; + dbg_sym_t *stp; + + if (IS_TYPE(tnum)) { + /* If (tnum == KLT_TYPE), then return the first type + * record, regardless of the type. Otherwise, search + * for the frst type that mapps into tnum. + */ + if ((stp = dbg_first_sym(DBG_TYPE))) { + kltp = (kltype_t *)stp->sym_kltype; + if (tnum != KLT_TYPE) { + while (kltp && !(kltp->kl_type & tnum)) { + if ((stp = dbg_next_sym(stp))) { + kltp = (kltype_t *)stp->sym_kltype; + } else { + kltp = (kltype_t *)NULL; + } + } + } + } + } else if (IS_TYPEDEF(tnum)) { + if ((stp = dbg_first_sym(DBG_TYPEDEF))) { + kltp = (kltype_t *)stp->sym_kltype; + } + } + return(kltp); +} + +/* + * kl_next_type() + */ +kltype_t * +kl_next_type(kltype_t *kltp) +{ + dbg_sym_t *stp, *nstp; + kltype_t *nkltp = (kltype_t *)NULL; + + if (!kltp) { + return((kltype_t *)NULL); + } + stp = (dbg_sym_t *)kltp->kl_ptr; + if ((nstp = dbg_next_sym(stp))) { + nkltp = (kltype_t *)nstp->sym_kltype; + } + return(nkltp); +} + +/* + * kl_prev_type() + */ +kltype_t * +kl_prev_type(kltype_t *kltp) +{ + dbg_sym_t *stp, *pstp; + kltype_t *pkltp = (kltype_t *)NULL; + + if (!kltp) { + return((kltype_t *)NULL); + } + stp = (dbg_sym_t *)kltp->kl_ptr; + if ((pstp = dbg_prev_sym(stp))) { + pkltp = (kltype_t *)pstp->sym_kltype; + } + return(pkltp); +} + +/* + * kl_realtype() + */ +kltype_t * +kl_realtype(kltype_t *kltp, int tnum) +{ + kltype_t *rkltp = kltp; + + while (rkltp) { + if (tnum && (rkltp->kl_type == tnum)) { + break; + } + if (!rkltp->kl_realtype) { + break; + } + if (rkltp->kl_realtype == rkltp) { + break; + } + rkltp = rkltp->kl_realtype; + if (rkltp == kltp) { + break; + } + } + return(rkltp); +} + +/* + * dbg_find_typenum() + */ +dbg_type_t * +dbg_find_typenum(uint64_t typenum) +{ + dbg_sym_t *stp; + dbg_type_t *sp = (dbg_type_t *)NULL; + + if ((stp = dbg_find_sym(0, DBG_TYPE, typenum))) { + sp = (dbg_type_t *)stp->sym_kltype; + } + return(sp); +} + +/* + * find type by typenum + */ +kltype_t * +kl_find_typenum(uint64_t typenum) +{ + kltype_t *kltp; + + kltp = (kltype_t *)dbg_find_typenum(typenum); + return(kltp); +} + +/* + * kl_find_btnode() -- non-recursive implementation. + */ +btnode_t * +_kl_find_btnode(btnode_t *np, char *key, int *max_depth, size_t len) +{ + int ret; + btnode_t *next, *prev; + + if (np) { + if (max_depth) { + (*max_depth)++; + } + next = np; +again: + if (len) { + ret = strncmp(key, next->bt_key, len); + } else { + ret = strcmp(key, next->bt_key); + } + if (ret == 0) { + if ((prev = kl_prev_btnode(next))) { + if (len) { + ret = strncmp(key, prev->bt_key, len); + } else { + ret = strcmp(key, prev->bt_key); + } + if (ret == 0) { + next = prev; + goto again; + } + } + return(next); + } else if (ret < 0) { + if ((next = next->bt_left)) { + goto again; + } + } else { + if ((next = next->bt_right)) { + goto again; + } + } + } + return((btnode_t *)NULL); +} + +/* + * kl_type_size() + */ +int +kl_type_size(kltype_t *kltp) +{ + kltype_t *rkltp; + + if (!kltp) { + return(0); + } + if (!(rkltp = kl_realtype(kltp, 0))) { + return(0); + } + return(rkltp->kl_size); +} + +/* + * kl_struct_len() + */ +int +kl_struct_len(char *s) +{ + kltype_t *kltp; + + if ((kltp = kl_find_type(s, (KLT_TYPES)))) { + return kl_type_size(kltp); + } + return(0); +} + +/* + * kl_get_member() + */ +kltype_t * +kl_get_member(kltype_t *kltp, char *f) +{ + kltype_t *mp; + + if ((mp = kltp->kl_member)) { + while (mp) { + if (mp->kl_flags & TYP_ANONYMOUS_FLG) { + kltype_t *amp; + + if ((amp = kl_get_member(mp->kl_realtype, f))) { + return(amp); + } + } else if (!strcmp(mp->kl_name, f)) { + break; + } + mp = mp->kl_member; + } + } + return(mp); +} + +/* + * kl_member() + */ +kltype_t * +kl_member(char *s, char *f) +{ + kltype_t *kltp, *mp = NULL; + + if (!(kltp = kl_find_type(s, (KLT_STRUCT|KLT_UNION)))) { + if ((kltp = kl_find_type(s, KLT_TYPEDEF))) { + kltp = kl_realtype(kltp, 0); + } + } + if (kltp) { + mp = kl_get_member(kltp, f); + } + return(mp); +} + + +/* + * kl_get_member_offset() + */ +int +kl_get_member_offset(kltype_t *kltp, char *f) +{ + kltype_t *mp; + + if ((mp = kltp->kl_member)) { + while (mp) { + if (mp->kl_flags & TYP_ANONYMOUS_FLG) { + int off; + + /* Drill down to see if the member we are looking for is in + * an anonymous union or struct. Since this call is recursive, + * the drill down may actually be multi-layer. + */ + off = kl_get_member_offset(mp->kl_realtype, f); + if (off >= 0) { + return(mp->kl_offset + off); + } + } else if (!strcmp(mp->kl_name, f)) { + return(mp->kl_offset); + } + mp = mp->kl_member; + } + } + return(-1); +} + +/* + * kl_member_offset() + */ +int +kl_member_offset(char *s, char *f) +{ + int off = -1; + kltype_t *kltp; + + if (!(kltp = kl_find_type(s, (KLT_STRUCT|KLT_UNION)))) { + if ((kltp = kl_find_type(s, KLT_TYPEDEF))) { + kltp = kl_realtype(kltp, 0); + } + } + if (kltp) { + off = kl_get_member_offset(kltp, f); + } + return(off); +} + +/* + * kl_is_member() + */ +int +kl_is_member(char *s, char *f) +{ + kltype_t *mp; + + if ((mp = kl_member(s, f))) { + return(1); + } + return(0); +} + +/* + * kl_member_size() + */ +int +kl_member_size(char *s, char *f) +{ + kltype_t *mp; + + if ((mp = kl_member(s, f))) { + return(mp->kl_size); + } + return(0); +} + +#define TAB_SPACES 8 +#define LEVEL_INDENT(level, flags) {\ + int i, j; \ + if (!(flags & NO_INDENT)) { \ + for (i = 0; i < level; i++) { \ + for (j = 0; j < TAB_SPACES; j++) { \ + kdb_printf(" "); \ + } \ + }\ + } \ +} +#define PRINT_NL(flags) \ + if (!(flags & SUPPRESS_NL)) { \ + kdb_printf("\n"); \ + } +#define PRINT_SEMI_COLON(level, flags) \ + if (level && (!(flags & SUPPRESS_SEMI_COLON))) { \ + kdb_printf(";"); \ + } + +/* + * print_realtype() + */ +static void +print_realtype(kltype_t *kltp) +{ + kltype_t *rkltp; + + if ((rkltp = kltp->kl_realtype)) { + while (rkltp && rkltp->kl_realtype) { + rkltp = rkltp->kl_realtype; + } + if (rkltp->kl_type == KLT_BASE) { + kdb_printf(" (%s)", rkltp->kl_name); + } + } +} + +int align_chk = 0; +/* + * kl_print_uint16() + * + */ +void +kl_print_uint16(void *ptr, int flags) +{ + unsigned long long a; + + /* Make sure the pointer is properly aligned (or we will + * * dump core) + * */ + if (align_chk && (uaddr_t)ptr % 16) { + kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); + return; + } + a = *(unsigned long long *) ptr; + if (flags & C_HEX) { + kdb_printf("%#llx", a); + } else if (flags & C_BINARY) { + kdb_printf("0b"); + kl_binary_print(a); + } else { + kdb_printf("%llu", a); + } +} + +#if 0 +/* + * kl_print_float16() + * + */ +void +kl_print_float16(void *ptr, int flags) +{ + double a; + + /* Make sure the pointer is properly aligned (or we will + * * dump core) + * */ + if (align_chk && (uaddr_t)ptr % 16) { + kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); + return; + } + a = *(double*) ptr; + kdb_printf("%f", a); +} +#endif + +/* + * kl_print_int16() + * + */ +void +kl_print_int16(void *ptr, int flags) +{ + long long a; + + /* Make sure the pointer is properly aligned (or we will + * * dump core) + * */ + if (align_chk && (uaddr_t)ptr % 16) { + kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); + return; + } + a = *(long long *) ptr; + if (flags & C_HEX) { + kdb_printf("%#llx", a); + } else if (flags & C_BINARY) { + kdb_printf("0b"); + kl_binary_print(a); + } else { + kdb_printf("%lld", a); + } +} + +/* + * kl_print_int8() + */ +void +kl_print_int8(void *ptr, int flags) +{ + long long a; + + /* Make sure the pointer is properly aligned (or we will + * dump core) + */ + if (align_chk && (uaddr_t)ptr % 8) { + kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); + return; + } + a = *(long long *) ptr; + if (flags & C_HEX) { + kdb_printf("%#llx", a); + } else if (flags & C_BINARY) { + kdb_printf("0b"); + kl_binary_print(a); + } else { + kdb_printf("%lld", a); + } +} + +#if 0 +/* + * kl_print_float8() + */ +void +kl_print_float8(void *ptr, int flags) +{ + double a; + + /* Make sure the pointer is properly aligned (or we will + * dump core) + */ + if (align_chk && (uaddr_t)ptr % 8) { + kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); + return; + } + a = *(double*) ptr; + kdb_printf("%f", a); +} +#endif + +/* + * kl_print_uint8() + */ +void +kl_print_uint8(void *ptr, int flags) +{ + unsigned long long a; + + /* Make sure the pointer is properly aligned (or we will + * dump core) + */ + if (align_chk && (uaddr_t)ptr % 8) { + kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); + return; + } + a = *(unsigned long long *) ptr; + if (flags & C_HEX) { + kdb_printf("%#llx", a); + } else if (flags & C_BINARY) { + kdb_printf("0b"); + kl_binary_print(a); + } else { + kdb_printf("%llu", a); + } +} + +/* + * kl_print_int4() + */ +void +kl_print_int4(void *ptr, int flags) +{ + int32_t a; + + /* Make sure the pointer is properly aligned (or we will + * dump core + */ + if (align_chk && (uaddr_t)ptr % 4) { + kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); + return; + } + a = *(int32_t*) ptr; + if (flags & C_HEX) { + kdb_printf("0x%x", a); + } else if (flags & C_BINARY) { + uint64_t value = a & 0xffffffff; + kdb_printf("0b"); + kl_binary_print(value); + } else { + kdb_printf("%d", a); + } +} + +#if 0 +/* + * kl_print_float4() + */ +void +kl_print_float4(void *ptr, int flags) +{ + float a; + + /* Make sure the pointer is properly aligned (or we will + * dump core) + */ + if (align_chk && (uaddr_t)ptr % 4) { + kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); + return; + } + a = *(float*) ptr; + kdb_printf("%f", a); +} +#endif + +/* + * kl_print_uint4() + */ +void +kl_print_uint4(void *ptr, int flags) +{ + uint32_t a; + + /* Make sure the pointer is properly aligned (or we will + * dump core) + */ + if (align_chk && (uaddr_t)ptr % 4) { + kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); + return; + } + a = *(uint32_t*) ptr; + if (flags & C_HEX) { + kdb_printf("0x%x", a); + } else if (flags & C_BINARY) { + uint64_t value = a & 0xffffffff; + kdb_printf("0b"); + kl_binary_print(value); + } else { + kdb_printf("%u", a); + } +} + +/* + * kl_print_int2() + */ +void +kl_print_int2(void *ptr, int flags) +{ + int16_t a; + + /* Make sure the pointer is properly aligned (or we will + * dump core + */ + if (align_chk && (uaddr_t)ptr % 2) { + kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); + return; + } + a = *(int16_t*) ptr; + if (flags & C_HEX) { + kdb_printf("0x%hx", a); + } else if (flags & C_BINARY) { + uint64_t value = a & 0xffff; + kdb_printf("0b"); + kl_binary_print(value); + } else { + kdb_printf("%hd", a); + } +} + +/* + * kl_print_uint2() + */ +void +kl_print_uint2(void *ptr, int flags) +{ + uint16_t a; + + /* Make sure the pointer is properly aligned (or we will + * dump core + */ + if (align_chk && (uaddr_t)ptr % 2) { + kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); + return; + } + a = *(uint16_t*) ptr; + if (flags & C_HEX) { + kdb_printf("0x%hx", a); + } else if (flags & C_BINARY) { + uint64_t value = a & 0xffff; + kdb_printf("0b"); + kl_binary_print(value); + } else { + kdb_printf("%hu", a); + } +} + +/* + * kl_print_char() + */ +void +kl_print_char(void *ptr, int flags) +{ + char c; + + if (flags & C_HEX) { + kdb_printf("0x%x", (*(char *)ptr) & 0xff); + } else if (flags & C_BINARY) { + uint64_t value = (*(char *)ptr) & 0xff; + kdb_printf("0b"); + kl_binary_print(value); + } else { + c = *(char *)ptr; + + kdb_printf("\'\\%03o\'", (unsigned char)c); + switch (c) { + case '\a' : + kdb_printf(" = \'\\a\'"); + break; + case '\b' : + kdb_printf(" = \'\\b\'"); + break; + case '\t' : + kdb_printf(" = \'\\t\'"); + break; + case '\n' : + kdb_printf(" = \'\\n\'"); + break; + case '\f' : + kdb_printf(" = \'\\f\'"); + break; + case '\r' : + kdb_printf(" = \'\\r\'"); + break; + case '\e' : + kdb_printf(" = \'\\e\'"); + break; + default : + if( !iscntrl((unsigned char) c) ) { + kdb_printf(" = \'%c\'", c); + } + break; + } + } +} + +/* + * kl_print_uchar() + */ +void +kl_print_uchar(void *ptr, int flags) +{ + if (flags & C_HEX) { + kdb_printf("0x%x", *(unsigned char *)ptr); + } else if (flags & C_BINARY) { + uint64_t value = (*(unsigned char *)ptr) & 0xff; + kdb_printf("0b"); + kl_binary_print(value); + } else { + kdb_printf("%u", *(unsigned char *)ptr); + } +} + +/* + * kl_print_base() + */ +void +kl_print_base(void *ptr, int size, int encoding, int flags) +{ + /* FIXME: untested */ + if (invalid_address((kaddr_t)ptr, size)) { + kdb_printf("ILLEGAL ADDRESS (%lx)", (uaddr_t)ptr); + return; + } + switch (size) { + + case 1: + if (encoding == ENC_UNSIGNED) { + kl_print_uchar(ptr, flags); + } else { + kl_print_char(ptr, flags); + } + break; + + case 2: + if (encoding == ENC_UNSIGNED) { + kl_print_uint2(ptr, flags); + } else { + kl_print_int2(ptr, flags); + } + break; + + case 4: + if (encoding == ENC_UNSIGNED) { + kl_print_uint4(ptr, flags); + } else if (encoding == ENC_FLOAT) { + printk("error: print of 4-byte float\n"); + /* kl_print_float4(ptr, flags); */ + } else { + kl_print_int4(ptr, flags); + } + break; + + case 8: + if (encoding == ENC_UNSIGNED) { + kl_print_uint8(ptr, flags); + } else if (encoding == ENC_FLOAT) { + printk("error: print of 8-byte float\n"); + /* kl_print_float8(ptr, flags); */ + } else { + kl_print_int8(ptr, flags); + } + break; + + case 16: + if (encoding == ENC_UNSIGNED) { + /* Ex: unsigned long long */ + kl_print_uint16(ptr, flags); + } else if (encoding == ENC_FLOAT) { + printk("error: print of 16-byte float\n"); + /* Ex: long double */ + /* kl_print_float16(ptr, flags); */ + } else { + /* Ex: long long */ + kl_print_int16(ptr, flags); + } + break; + + default: + break; + } +} + +/* + * kl_print_base_value() + */ +void +kl_print_base_value(void *ptr, kltype_t *kltp, int flags) +{ + kltype_t *rkltp=NULL; + + if (kltp->kl_type != KLT_BASE) { + if (!(rkltp = kltp->kl_realtype)) { + return; + } + if (rkltp->kl_type != KLT_BASE) { + return; + } + } else { + rkltp = kltp; + } + kl_print_base(ptr, rkltp->kl_size, rkltp->kl_encoding, flags); +} + +/* + * kl_print_typedef_type() + */ +void +kl_print_typedef_type( + void *ptr, + kltype_t *kltp, + int level, + int flags) +{ + char *name; + kltype_t *rkltp; + + if (ptr) { + rkltp = kltp->kl_realtype; + while (rkltp->kl_type == KLT_TYPEDEF) { + if (rkltp->kl_realtype) { + rkltp = rkltp->kl_realtype; + } + } + if (rkltp->kl_type == KLT_POINTER) { + kl_print_pointer_type(ptr, kltp, level, flags); + return; + } + switch (rkltp->kl_type) { + case KLT_BASE: + kl_print_base_type(ptr, kltp, + level, flags); + break; + + case KLT_UNION: + case KLT_STRUCT: + kl_print_struct_type(ptr, kltp, + level, flags); + break; + + case KLT_ARRAY: + kl_print_array_type(ptr, kltp, + level, flags); + break; + + case KLT_ENUMERATION: + kl_print_enumeration_type(ptr, + kltp, level, flags); + break; + + default: + kl_print_base_type(ptr, kltp, + level, flags); + break; + } + } else { + LEVEL_INDENT(level, flags); + if (flags & NO_REALTYPE) { + rkltp = kltp; + } else { + rkltp = kltp->kl_realtype; + while (rkltp && rkltp->kl_type == KLT_POINTER) { + rkltp = rkltp->kl_realtype; + } + } + if (!rkltp) { + if (SUPPRESS_NAME) { + kdb_printf(""); + } else { + kdb_printf( "typedef %s;", + kltp->kl_name); + } + return; + } + if (rkltp->kl_type == KLT_FUNCTION) { + if (kltp->kl_realtype->kl_type == KLT_POINTER) { + kdb_printf("typedef %s(*%s)();", + kltp->kl_typestr, kltp->kl_name); + } else { + kdb_printf( "typedef %s(%s)();", + kltp->kl_typestr, kltp->kl_name); + } + } else if (rkltp->kl_type == KLT_ARRAY) { + kl_print_array_type(ptr, rkltp, level, flags); + } else if (rkltp->kl_type == KLT_TYPEDEF) { + if (!(name = rkltp->kl_name)) { + name = rkltp->kl_typestr; + } + + if (SUPPRESS_NAME) { + kdb_printf("%s", name); + } else { + kdb_printf("typedef %s%s;", + name, kltp->kl_name); + } + print_realtype(rkltp); + } else { + kl_print_type(ptr, rkltp, level, flags); + } + PRINT_NL(flags); + } +} + +/* + * kl_print_pointer_type() + */ +void +kl_print_pointer_type( + void *ptr, + kltype_t *kltp, + int level, + int flags) +{ + kltype_t *itp; + + if (kltp->kl_type == KLT_MEMBER) { + itp = kltp->kl_realtype; + } else { + itp = kltp; + } + + /* See if this is a pointer to a function. If it is, then it + * has to be handled differently... + */ + while (itp->kl_type == KLT_POINTER) { + if ((itp = itp->kl_realtype)) { + if (itp->kl_type == KLT_FUNCTION) { + kl_print_function_type(ptr, + kltp, level, flags); + return; + } + } else { + LEVEL_INDENT(level, flags); + kdb_printf("%s%s;\n", + kltp->kl_typestr, kltp->kl_name); + return; + } + } + + LEVEL_INDENT(level, flags); + if (ptr) { + kaddr_t tmp; + tmp = *(kaddr_t *)ptr; + flags |= SUPPRESS_SEMI_COLON; + if(kltp->kl_name){ + if (*(kaddr_t *)ptr) { + kdb_printf("%s = 0x%"FMTPTR"x", + kltp->kl_name, tmp); + } else { + kdb_printf("%s = (nil)", kltp->kl_name); + } + } else { + if (tmp != 0) { + kdb_printf("0x%"FMTPTR"x", tmp); + } else { + kdb_printf( "(nil)"); + } + } + } else { + if (kltp->kl_typestr) { + if (kltp->kl_name && !(flags & SUPPRESS_NAME)) { + kdb_printf("%s%s", + kltp->kl_typestr, kltp->kl_name); + } else { + kdb_printf("%s", kltp->kl_typestr); + } + } else { + kdb_printf(""); + } + } + PRINT_SEMI_COLON(level, flags); + PRINT_NL(flags); +} + +/* + * kl_print_function_type() + */ +void +kl_print_function_type( + void *ptr, + kltype_t *kltp, + int level, + int flags) +{ + LEVEL_INDENT(level, flags); + if (ptr) { + kaddr_t a; + + a = *(kaddr_t *)ptr; + kdb_printf("%s = 0x%"FMTPTR"x", kltp->kl_name, a); + } else { + if (flags & SUPPRESS_NAME) { + kdb_printf("%s(*)()", kltp->kl_typestr); + } else { + kdb_printf("%s(*%s)();", + kltp->kl_typestr, kltp->kl_name); + } + } + PRINT_NL(flags); +} + +/* + * kl_print_array_type() + */ +void +kl_print_array_type(void *ptr, kltype_t *kltp, int level, int flags) +{ + int i, count = 0, anon = 0, size, low, high, multi = 0; + char typestr[128], *name, *p; + kltype_t *rkltp, *etp, *retp; + + if (kltp->kl_type != KLT_ARRAY) { + if ((rkltp = kltp->kl_realtype)) { + while (rkltp->kl_type != KLT_ARRAY) { + if (!(rkltp = rkltp->kl_realtype)) { + break; + } + } + } + if (!rkltp) { + LEVEL_INDENT(level, flags); + kdb_printf(""); + PRINT_SEMI_COLON(level, flags); + PRINT_NL(flags); + return; + } + } else { + rkltp = kltp; + } + + etp = rkltp->kl_elementtype; + if (!etp) { + LEVEL_INDENT(level, flags); + kdb_printf(" %s", rkltp->kl_name); + PRINT_SEMI_COLON(level, flags); + PRINT_NL(flags); + return; + } + + /* Set retp to point to the actual element type. This is necessary + * for multi-dimensional arrays, which link using the kl_elementtype + * member. + */ + retp = etp; + while (retp->kl_type == KLT_ARRAY) { + retp = retp->kl_elementtype; + } + low = rkltp->kl_low_bounds + 1; + high = rkltp->kl_high_bounds; + + if (ptr) { + + p = ptr; + + if ((retp->kl_size == 1) && (retp->kl_encoding == ENC_CHAR)) { + if (kltp->kl_type == KLT_MEMBER) { + LEVEL_INDENT(level, flags); + } + if (flags & SUPPRESS_NAME) { + kdb_printf("\""); + flags &= ~SUPPRESS_NAME; + } else { + kdb_printf("%s = \"", kltp->kl_name); + } + for (i = 0; i < high; i++) { + if (*(char*)p == 0) { + break; + } + kdb_printf("%c", *(char *)p); + p++; + } + kdb_printf("\""); + PRINT_NL(flags); + } else { + if (kltp->kl_type == KLT_MEMBER) { + LEVEL_INDENT(level, flags); + } + + if (flags & SUPPRESS_NAME) { + kdb_printf("{\n"); + flags &= ~SUPPRESS_NAME; + } else { + kdb_printf("%s = {\n", kltp->kl_name); + } + + if (retp->kl_type == KLT_POINTER) { + size = sizeof(void *); + } else { + while (retp->kl_realtype) { + retp = retp->kl_realtype; + } + size = retp->kl_size; + } + if ((retp->kl_type != KLT_STRUCT) && + (retp->kl_type != KLT_UNION)) { + /* Turn off the printing of names for all + * but structs and unions. + */ + flags |= SUPPRESS_NAME; + } + for (i = low; i <= high; i++) { + + LEVEL_INDENT(level + 1, flags); + kdb_printf("[%d] ", i); + + switch (retp->kl_type) { + case KLT_POINTER : + kl_print_pointer_type( + p, retp, level, + flags|NO_INDENT); + break; + + case KLT_TYPEDEF: + kl_print_typedef_type( + p, retp, level, + flags|NO_INDENT); + break; + + case KLT_BASE: + kl_print_base_value(p, + retp, flags|NO_INDENT); + kdb_printf("\n"); + break; + + case KLT_ARRAY: + kl_print_array_type(p, retp, + level + 1, + flags|SUPPRESS_NAME); + break; + + case KLT_STRUCT: + case KLT_UNION: + kl_print_struct_type(p, + retp, level + 1, + flags|NO_INDENT); + break; + + default: + kl_print_base_value( + p, retp, + flags|NO_INDENT); + kdb_printf("\n"); + break; + } + p = (void *)((uaddr_t)p + size); + } + LEVEL_INDENT(level, flags); + kdb_printf("}"); + PRINT_SEMI_COLON(level, flags); + PRINT_NL(flags); + } + } else { + if (rkltp) { + count = (rkltp->kl_high_bounds - + rkltp->kl_low_bounds) + 1; + } else { + count = 1; + } + + if (!strcmp(retp->kl_typestr, "struct ") || + !strcmp(retp->kl_typestr, "union ")) { + anon = 1; + } +next_dimension: + switch (retp->kl_type) { + + case KLT_UNION: + case KLT_STRUCT: + if (anon) { + if (multi) { + kdb_printf("[%d]", count); + break; + } + kl_print_struct_type(ptr, retp, level, + flags| + SUPPRESS_NL| + SUPPRESS_SEMI_COLON); + if (kltp->kl_type == KLT_MEMBER) { + kdb_printf(" %s[%d]", + kltp->kl_name, count); + } else { + kdb_printf(" [%d]", count); + } + break; + } + /* else drop through */ + + default: + LEVEL_INDENT(level, flags); + if (multi) { + kdb_printf("[%d]", count); + break; + } + name = kltp->kl_name; + if (retp->kl_type == KLT_TYPEDEF) { + strcpy(typestr, retp->kl_name); + strcat(typestr, " "); + } else { + strcpy(typestr, retp->kl_typestr); + } + if (!name || (flags & SUPPRESS_NAME)) { + kdb_printf("%s[%d]", typestr, count); + } else { + kdb_printf("%s%s[%d]", + typestr, name, count); + } + } + if (etp->kl_type == KLT_ARRAY) { + count = etp->kl_high_bounds - etp->kl_low_bounds + 1; + etp = etp->kl_elementtype; + multi++; + goto next_dimension; + } + PRINT_SEMI_COLON(level, flags); + PRINT_NL(flags); + } +} + +/* + * kl_print_enumeration_type() + */ +void +kl_print_enumeration_type( + void *ptr, + kltype_t *kltp, + int level, + int flags) +{ + unsigned long long val = 0; + kltype_t *mp, *rkltp; + + rkltp = kl_realtype(kltp, KLT_ENUMERATION); + if (ptr) { + switch (kltp->kl_size) { + case 1: + val = *(unsigned long long *)ptr; + break; + + case 2: + val = *(uint16_t *)ptr; + break; + + case 4: + val = *(uint32_t *)ptr; + break; + + case 8: + val = *(uint64_t *)ptr; + break; + } + mp = rkltp->kl_member; + while (mp) { + if (mp->kl_value == val) { + break; + } + mp = mp->kl_member; + } + LEVEL_INDENT(level, flags); + if (mp) { + kdb_printf("%s = (%s=%lld)", + kltp->kl_name, mp->kl_name, val); + } else { + kdb_printf("%s = %lld", kltp->kl_name, val); + } + PRINT_NL(flags); + } else { + LEVEL_INDENT(level, flags); + kdb_printf ("%s {", kltp->kl_typestr); + mp = rkltp->kl_member; + while (mp) { + kdb_printf("%s = %d", mp->kl_name, mp->kl_value); + if ((mp = mp->kl_member)) { + kdb_printf(", "); + } + } + mp = kltp; + if (level) { + kdb_printf("} %s;", mp->kl_name); + } else { + kdb_printf("};"); + } + PRINT_NL(flags); + } +} + +/* + * kl_binary_print() + */ +void +kl_binary_print(uint64_t num) +{ + int i, pre = 1; + + for (i = 63; i >= 0; i--) { + if (num & ((uint64_t)1 << i)) { + kdb_printf("1"); + if (pre) { + pre = 0; + } + } else { + if (!pre) { + kdb_printf("0"); + } + } + } + if (pre) { + kdb_printf("0"); + } +} + +/* + * kl_get_bit_value() + * + * x = byte_size, y = bit_size, z = bit_offset + */ +uint64_t +kl_get_bit_value(void *ptr, unsigned int x, unsigned int y, unsigned int z) +{ + uint64_t value=0, mask; + + /* handle x bytes of buffer -- doing just memcpy won't work + * on big endian architectures + */ + switch (x) { + case 5: + case 6: + case 7: + case 8: + x = 8; + value = *(uint64_t*) ptr; + break; + case 3: + case 4: + x = 4; + value = *(uint32_t*) ptr; + break; + case 2: + value = *(uint16_t*) ptr; + break; + case 1: + value = *(uint8_t *)ptr; + break; + default: + /* FIXME: set KL_ERROR */ + return(0); + } + /* + o FIXME: correct handling of overlapping fields + */ + + /* goto bit offset */ + value = value >> z; + + /* mask bit size bits */ + mask = (((uint64_t)1 << y) - 1); + return (value & mask); +} + +/* + * kl_print_bit_value() + * + * x = byte_size, y = bit_size, z = bit_offset + */ +void +kl_print_bit_value(void *ptr, int x, int y, int z, int flags) +{ + unsigned long long value; + + value = kl_get_bit_value(ptr, x, y, z); + if (flags & C_HEX) { + kdb_printf("%#llx", value); + } else if (flags & C_BINARY) { + kdb_printf("0b"); + kl_binary_print(value); + } else { + kdb_printf("%lld", value); + } +} + +/* + * kl_print_base_type() + */ +void +kl_print_base_type(void *ptr, kltype_t *kltp, int level, int flags) +{ + LEVEL_INDENT(level, flags); + if (ptr) { + if (!(flags & SUPPRESS_NAME)) { + kdb_printf ("%s = ", kltp->kl_name); + } + } + if (kltp->kl_type == KLT_MEMBER) { + if (kltp->kl_bit_size < (kltp->kl_size * 8)) { + if (ptr) { + kl_print_bit_value(ptr, kltp->kl_size, + kltp->kl_bit_size, + kltp->kl_bit_offset, flags); + } else { + if (kltp->kl_name) { + kdb_printf ("%s%s :%d;", + kltp->kl_typestr, + kltp->kl_name, + kltp->kl_bit_size); + } else { + kdb_printf ("%s :%d;", + kltp->kl_typestr, + kltp->kl_bit_size); + } + } + PRINT_NL(flags); + return; + } + } + if (ptr) { + kltype_t *rkltp; + + rkltp = kl_realtype(kltp, 0); + if (rkltp->kl_encoding == ENC_UNDEFINED) { + /* This is a void value + */ + kdb_printf(""); + } else { + kl_print_base(ptr, kltp->kl_size, + rkltp->kl_encoding, flags); + } + } else { + if (kltp->kl_type == KLT_MEMBER) { + if (flags & SUPPRESS_NAME) { + kdb_printf ("%s", kltp->kl_typestr); + } else { + if (kltp->kl_name) { + kdb_printf("%s%s;", kltp->kl_typestr, + kltp->kl_name); + } else { + kdb_printf ("%s :%d;", + kltp->kl_typestr, + kltp->kl_bit_size); + } + } + } else { + if (SUPPRESS_NAME) { + kdb_printf("%s", kltp->kl_name); + } else { + kdb_printf("%s;", kltp->kl_name); + } + } + } + PRINT_NL(flags); +} + +/* + * kl_print_member() + */ +void +kl_print_member(void *ptr, kltype_t *mp, int level, int flags) +{ + int kl_type = 0; + kltype_t *rkltp; + + if (flags & C_SHOWOFFSET) { + kdb_printf("%#x ", mp->kl_offset); + } + + if ((rkltp = mp->kl_realtype)) { + kl_type = rkltp->kl_type; + } else + kl_type = mp->kl_type; + switch (kl_type) { + case KLT_STRUCT: + case KLT_UNION: + kl_print_struct_type(ptr, mp, level, flags); + break; + case KLT_ARRAY: + kl_print_array_type(ptr, mp, level, flags); + break; + case KLT_POINTER: + kl_print_pointer_type(ptr, mp, level, flags); + break; + case KLT_FUNCTION: + kl_print_function_type(ptr, mp, level, flags); + break; + case KLT_BASE: + kl_print_base_type(ptr, mp, level, flags); + break; + case KLT_ENUMERATION: + kl_print_enumeration_type(ptr, mp, level, flags); + break; + case KLT_TYPEDEF: + while (rkltp && rkltp->kl_realtype) { + if (rkltp->kl_realtype == rkltp) { + break; + } + rkltp = rkltp->kl_realtype; + } + if (ptr) { + kl_print_typedef_type(ptr, mp, + level, flags); + break; + } + LEVEL_INDENT(level, flags); + if (flags & SUPPRESS_NAME) { + if (rkltp && (mp->kl_bit_size < + (rkltp->kl_size * 8))) { + kdb_printf ("%s :%d", + mp->kl_typestr, + mp->kl_bit_size); + } else { + kdb_printf("%s", + mp->kl_realtype->kl_name); + } + print_realtype(mp->kl_realtype); + } else { + if (rkltp && (mp->kl_bit_size < + (rkltp->kl_size * 8))) { + if (mp->kl_name) { + kdb_printf ("%s%s :%d;", + mp->kl_typestr, + mp->kl_name, + mp->kl_bit_size); + } else { + kdb_printf ("%s :%d;", + mp->kl_typestr, + mp->kl_bit_size); + } + } else { + kdb_printf("%s %s;", + mp->kl_realtype->kl_name, + mp->kl_name); + } + } + PRINT_NL(flags); + break; + + default: + LEVEL_INDENT(level, flags); + if (mp->kl_typestr) { + kdb_printf("%s%s;", + mp->kl_typestr, mp->kl_name); + } else { + kdb_printf("<\?\?\? kl_type:%d> %s;", + kl_type, mp->kl_name); + } + PRINT_NL(flags); + break; + } +} + +/* + * kl_print_struct_type() + */ +void +kl_print_struct_type(void *buf, kltype_t *kltp, int level, int flags) +{ + int orig_flags = flags; + void *ptr = NULL; + kltype_t *mp, *rkltp; + + /* If we are printing out an actual struct, then don't print any + * semi colons. + */ + if (buf) { + flags |= SUPPRESS_SEMI_COLON; + } + + LEVEL_INDENT(level, flags); + if ((level == 0) || (flags & NO_INDENT)) { + kdb_printf("%s{\n", kltp->kl_typestr); + } else { + if (buf) { + if (level && !(kltp->kl_flags & TYP_ANONYMOUS_FLG)) { + kdb_printf("%s = %s{\n", + kltp->kl_name, kltp->kl_typestr); + } else { + kdb_printf("%s{\n", kltp->kl_typestr); + } + flags &= (~SUPPRESS_NL); + } else { + if (kltp->kl_typestr) { + kdb_printf("%s{\n", kltp->kl_typestr); + } else { + kdb_printf(" {\n"); + } + } + } + + /* If the SUPPRESS_NL, SUPPRESS_SEMI_COLON, and SUPPRESS_NAME flags + * are set and buf is NULL, then turn them off as they only apply + * at the end of the struct. We save the original flags for that + * purpose. + */ + if (!buf) { + flags &= ~(SUPPRESS_NL|SUPPRESS_SEMI_COLON|SUPPRESS_NAME); + } + + /* If the NO_INDENT is set, we need to turn it off at this + * point -- just in case we come across a member of this struct + * that is also a struct. + */ + if (flags & NO_INDENT) { + flags &= ~(NO_INDENT); + } + + if (kltp->kl_type == KLT_MEMBER) { + rkltp = kl_realtype(kltp, 0); + } else { + rkltp = kltp; + } + level++; + if ((mp = rkltp->kl_member)) { + while (mp) { + if (buf) { + ptr = buf + mp->kl_offset; + } + kl_print_member(ptr, mp, level, flags); + mp = mp->kl_member; + } + } else { + if (kltp->kl_flags & TYP_INCOMPLETE_FLG) { + LEVEL_INDENT(level, flags); + kdb_printf("\n"); + } + } + level--; + LEVEL_INDENT(level, flags); + + /* kl_size = 0 for empty structs */ + if (ptr || ((kltp->kl_size == 0) && buf)) { + kdb_printf("}"); + } else if ((kltp->kl_type == KLT_MEMBER) && + !(orig_flags & SUPPRESS_NAME) && + !(kltp->kl_flags & TYP_ANONYMOUS_FLG)) { + kdb_printf("} %s", kltp->kl_name); + } else { + kdb_printf("}"); + } + PRINT_SEMI_COLON(level, orig_flags); + PRINT_NL(orig_flags); +} + +/* + * kl_print_type() + */ +void +kl_print_type(void *buf, kltype_t *kltp, int level, int flags) +{ + void *ptr; + + if (buf) { + if (kltp->kl_offset) { + ptr = (void *)((uaddr_t)buf + kltp->kl_offset); + } else { + ptr = buf; + } + } else { + ptr = 0; + } + + /* Only allow binary printing for base types + */ + if (kltp->kl_type != KLT_BASE) { + flags &= (~C_BINARY); + } + switch (kltp->kl_type) { + + case KLT_TYPEDEF: + kl_print_typedef_type(ptr, kltp, level, flags); + break; + + case KLT_STRUCT: + case KLT_UNION: + kl_print_struct_type(ptr, kltp, level, flags); + break; + + case KLT_MEMBER: + kl_print_member(ptr, kltp, level, flags); + break; + + case KLT_POINTER: + kl_print_pointer_type(ptr, kltp, level, flags); + break; + + case KLT_FUNCTION: + LEVEL_INDENT(level, flags); + kl_print_function_type(ptr, kltp, level, flags); + break; + + case KLT_ARRAY: + kl_print_array_type(ptr, kltp, level, flags); + break; + + case KLT_ENUMERATION: + kl_print_enumeration_type(ptr, + kltp, level, flags); + break; + + case KLT_BASE: + kl_print_base_type(ptr, kltp, level, flags); + break; + + default: + LEVEL_INDENT(level, flags); + if (flags & SUPPRESS_NAME) { + kdb_printf ("%s", kltp->kl_name); + } else { + kdb_printf ("%s %s;", + kltp->kl_name, kltp->kl_name); + } + PRINT_NL(flags); + } +} + +/* + * eval is from lcrash eval.c + */ + +/* Forward declarations */ +static void free_node(node_t *); +static node_t *make_node(token_t *, int); +static node_t *get_node_list(token_t *, int); +static node_t *do_eval(int); +static int is_unary(int); +static int is_binary(int); +static int precedence(int); +static node_t *get_sizeof(void); +static int replace_cast(node_t *, int); +static int replace_unary(node_t *, int); +static node_t *replace(node_t *, int); +static void array_to_element(node_t*, node_t*); +static int type_to_number(node_t *); +kltype_t *number_to_type(node_t *); +static type_t *eval_type(node_t *); +static type_t *get_type(char *, int); +static int add_rchild(node_t *, node_t *); +static void free_nodelist(node_t *); + +/* Global variables + */ +static int logical_flag; +static node_t *node_list = (node_t *)NULL; +uint64_t eval_error; +char *error_token; + +/* + * set_eval_error() + */ +static void +set_eval_error(uint64_t ecode) +{ + eval_error = ecode; +} + +/* + * is_typestr() + * + * We check for "struct", "union", etc. separately because they + * would not be an actual part of the type name. We also assume + * that the string passed in + * + * - does not have any leading blanks or tabs + * - is NULL terminated + * - contains only one type name to check + * - does not contain any '*' characters + */ +static int +is_typestr(char *str) +{ + int len; + + len = strlen(str); + if ((len >= 6) && !strncmp(str, "struct", 6)) { + return(1); + } else if ((len >= 5) &&!strncmp(str, "union", 5)) { + return(1); + } else if ((len >= 5) &&!strncmp(str, "short", 5)) { + return(1); + } else if ((len >= 8) &&!strncmp(str, "unsigned", 8)) { + return(1); + } else if ((len >= 6) &&!strncmp(str, "signed", 6)) { + return(1); + } else if ((len >= 4) &&!strncmp(str, "long", 4)) { + return(1); + } + /* Strip off any trailing blanks + */ + while(*str && ((str[strlen(str) - 1] == ' ') + || (str[strlen(str) - 1] == '\t'))) { + str[strlen(str) - 1] = 0; + } + if (kl_find_type(str, KLT_TYPES)) { + return (1); + } + return(0); +} + +/* + * free_tokens() + */ +static void +free_tokens(token_t *tp) +{ + token_t *t, *tnext; + + t = tp; + while (t) { + tnext = t->next; + if (t->string) { + kl_free_block((void *)t->string); + } + kl_free_block((void *)t); + t = tnext; + } +} + +/* + * process_text() + */ +static int +process_text(char **str, token_t *tok) +{ + char *cp = *str; + char *s = NULL; + int len = 0; + + /* Check and see if this token is a STRING or CHARACTER + * type (beginning with a single or double quote). + */ + if (*cp == '\'') { + /* make sure that only a single character is between + * the single quotes (it can be an escaped character + * too). + */ + s = strpbrk((cp + 1), "\'"); + if (!s) { + set_eval_error(E_SINGLE_QUOTE); + error_token = tok->ptr; + return(1); + } + len = (uaddr_t)s - (uaddr_t)cp; + if ((*(cp+1) == '\\')) { + if (*(cp+2) == '0') { + long int val; + unsigned long uval; + char *ep; + + uval = kl_strtoull((char*)(cp+2), + (char **)&ep, 8); + val = uval; + if ((val > 255) || (*ep != '\'')) { + set_eval_error(E_BAD_CHAR); + error_token = tok->ptr; + return(1); + } + } else if (*(cp+3) != '\'') { + set_eval_error(E_BAD_CHAR); + error_token = tok->ptr; + return(1); + } + tok->type = CHARACTER; + } else if (len == 2) { + tok->type = CHARACTER; + } else { + + /* Treat as a single token entry. It's possible + * that what's between the single quotes is a + * type name. That will be determined later on. + */ + tok->type = STRING; + } + *str = cp + len; + } else if (*cp == '\"') { + s = strpbrk((cp + 1), "\""); + if (!s) { + set_eval_error(E_BAD_STRING); + error_token = tok->ptr; + return(1); + } + len = (uaddr_t)s - (uaddr_t)cp; + tok->type = TEXT; + *str = cp + len; + } + if ((tok->type == STRING) || (tok->type == TEXT)) { + + if ((tok->type == TEXT) && (strlen(cp) > (len + 1))) { + + /* Check to see if there is a comma or semi-colon + * directly following the string. If there is, + * then the string is OK (the following characters + * are part of the next expression). Also, it's OK + * to have trailing blanks as long as that's all + * threre is. + */ + char *c; + + c = s + 1; + while (*c) { + if ((*c == ',') || (*c == ';')) { + break; + } else if (*c != ' ') { + set_eval_error(E_END_EXPECTED); + tok->ptr = c; + error_token = tok->ptr; + return(1); + } + c++; + } + /* Truncate the trailing blanks (they are not + * part of the string). + */ + if (c != (s + 1)) { + *(s + 1) = 0; + } + } + tok->string = (char *)kl_alloc_block(len); + memcpy(tok->string, (cp + 1), len - 1); + tok->string[len - 1] = 0; + } + return(0); +} + +/* + * get_token_list() + */ +static token_t * +get_token_list(char *str) +{ + int paren_count = 0; + char *cp; + token_t *tok = (token_t*)NULL, *tok_head = (token_t*)NULL; + token_t *tok_last = (token_t*)NULL; + + cp = str; + eval_error = 0; + + while (*cp) { + + /* Skip past any "white space" (spaces and tabs). + */ + switch (*cp) { + case ' ' : + case '\t' : + case '`' : + cp++; + continue; + default : + break; + } + + /* Allocate space for the next token */ + tok = (token_t *)kl_alloc_block(sizeof(token_t)); + tok->ptr = cp; + + switch(*cp) { + + /* Check for operators + */ + case '+' : + if (*((char*)cp + 1) == '+') { + + /* We aren't doing asignment here, + * so the ++ operator is not + * considered valid. + */ + set_eval_error(E_BAD_OPERATOR); + error_token = tok_last->ptr; + free_tokens(tok_head); + free_tokens(tok); + return ((token_t*)NULL); + } else if (!tok_last || + (tok_last->operator && + (tok_last->operator != CLOSE_PAREN))) { + tok->operator = UNARY_PLUS; + } else { + tok->operator = ADD; + } + break; + + case '-' : + if (*((char*)cp + 1) == '-') { + + /* We aren't doing asignment here, so + * the -- operator is not considered + * valid. + */ + set_eval_error(E_BAD_OPERATOR); + error_token = tok_last->ptr; + free_tokens(tok_head); + free_tokens(tok); + return ((token_t*)NULL); + } else if (*((char*)cp + 1) == '>') { + tok->operator = RIGHT_ARROW; + cp++; + } else if (!tok_last || (tok_last->operator && + (tok_last->operator != CLOSE_PAREN))) { + tok->operator = UNARY_MINUS; + } else { + tok->operator = SUBTRACT; + } + break; + + case '.' : + /* XXX - need to check to see if this is a + * decimal point in the middle fo a floating + * point value. + */ + tok->operator = DOT; + break; + + case '*' : + /* XXX - need a better way to tell if this is + * an INDIRECTION. perhaps check the next + * token? + */ + if (!tok_last || (tok_last->operator && + ((tok_last->operator != CLOSE_PAREN) && + (tok_last->operator != CAST)))) { + tok->operator = INDIRECTION; + } else { + tok->operator = MULTIPLY; + } + break; + + case '/' : + tok->operator = DIVIDE; + break; + + case '%' : + tok->operator = MODULUS; + break; + + case '(' : { + char *s, *s1, *s2; + int len; + + /* Make sure the previous token is an operator + */ + if (tok_last && !tok_last->operator) { + set_eval_error(E_SYNTAX_ERROR); + error_token = tok_last->ptr; + free_tokens(tok_head); + free_tokens(tok); + return ((token_t*)NULL); + } + + if (tok_last && + ((tok_last->operator == RIGHT_ARROW) || + (tok_last->operator == DOT))) { + set_eval_error(E_SYNTAX_ERROR); + error_token = tok_last->ptr; + free_tokens(tok_head); + free_tokens(tok); + return ((token_t*)NULL); + } + + /* Check here to see if following tokens + * constitute a cast. + */ + + /* Skip past any "white space" (spaces + * and tabs) + */ + while ((*(cp+1) == ' ') || (*(cp+1) == '\t')) { + cp++; + } + if ((*(cp+1) == '(') || isdigit(*(cp+1)) || + (*(cp+1) == '+') || (*(cp+1) == '-') || + (*(cp+1) == '*') || (*(cp+1) == '&') || + (*(cp+1) == ')')){ + tok->operator = OPEN_PAREN; + paren_count++; + break; + } + + /* Make sure we have a CLOSE_PAREN. + */ + if (!(s1 = strchr(cp+1, ')'))) { + set_eval_error(E_OPEN_PAREN); + error_token = tok->ptr; + free_tokens(tok_head); + free_tokens(tok); + return ((token_t*)NULL); + } + /* Check to see if this is NOT a simple + * typecast. + */ + if (!(s2 = strchr(cp+1, '.'))) { + s2 = strstr(cp+1, "->"); + } + if (s2 && (s2 < s1)) { + tok->operator = OPEN_PAREN; + paren_count++; + break; + } + + if ((s = strpbrk(cp+1, "*)"))) { + char str[128]; + + len = (uaddr_t)s - (uaddr_t)(cp+1); + strncpy(str, cp+1, len); + str[len] = 0; + if (!is_typestr(str)) { + set_eval_error(E_BAD_TYPE); + error_token = tok->ptr; + free_tokens(tok_head); + free_tokens(tok); + return ((token_t*)NULL); + } + if (!(s = strpbrk((cp+1), ")"))) { + set_eval_error(E_OPEN_PAREN); + error_token = tok->ptr; + free_tokens(tok_head); + free_tokens(tok); + return ((token_t*)NULL); + } + len = (uaddr_t)s - (uaddr_t)(cp+1); + tok->string = (char *) + kl_alloc_block(len + 1); + memcpy(tok->string, (cp+1), len); + tok->string[len] = 0; + tok->operator = CAST; + cp = (char *)((uaddr_t)(cp+1) + len); + break; + } + tok->operator = OPEN_PAREN; + paren_count++; + break; + } + + case ')' : + if (tok_last && ((tok_last->operator == + RIGHT_ARROW) || + (tok_last->operator == DOT))) { + set_eval_error(E_SYNTAX_ERROR); + error_token = tok_last->ptr; + free_tokens(tok_head); + free_tokens(tok); + return ((token_t*)NULL); + } + tok->operator = CLOSE_PAREN; + paren_count--; + break; + + case '&' : + if (*((char*)cp + 1) == '&') { + tok->operator = LOGICAL_AND; + cp++; + } else if (!tok_last || (tok_last && + (tok_last->operator && + tok_last->operator != + CLOSE_PAREN))) { + tok->operator = ADDRESS; + } else { + tok->operator = BITWISE_AND; + } + break; + + case '|' : + if (*((char*)cp + 1) == '|') { + tok->operator = LOGICAL_OR; + cp++; + } else { + tok->operator = BITWISE_OR; + } + break; + + case '=' : + if (*((char*)cp + 1) == '=') { + tok->operator = EQUAL; + cp++; + } else { + /* ASIGNMENT -- NOT IMPLEMENTED + */ + tok->operator = NOT_YET; + } + break; + + case '<' : + if (*((char*)cp + 1) == '<') { + tok->operator = LEFT_SHIFT; + cp++; + } else if (*((char*)cp + 1) == '=') { + tok->operator = LESS_THAN_OR_EQUAL; + cp++; + } else { + tok->operator = LESS_THAN; + } + break; + + case '>' : + if (*((char*)(cp + 1)) == '>') { + tok->operator = RIGHT_SHIFT; + cp++; + } else if (*((char*)cp + 1) == '=') { + tok->operator = GREATER_THAN_OR_EQUAL; + cp++; + } else { + tok->operator = GREATER_THAN; + } + break; + + case '!' : + if (*((char*)cp + 1) == '=') { + tok->operator = NOT_EQUAL; + cp++; + } else { + tok->operator = LOGICAL_NEGATION; + } + break; + + case '$' : + set_eval_error(E_NOT_IMPLEMENTED); + error_token = tok->ptr; + free_tokens(tok_head); + free_tokens(tok); + return((token_t*)NULL); + case '~' : + tok->operator = ONES_COMPLEMENT; + break; + + case '^' : + tok->operator = BITWISE_EXCLUSIVE_OR; + break; + + case '?' : + set_eval_error(E_NOT_IMPLEMENTED); + error_token = tok->ptr; + free_tokens(tok_head); + free_tokens(tok); + return((token_t*)NULL); + case ':' : + set_eval_error(E_NOT_IMPLEMENTED); + error_token = tok->ptr; + free_tokens(tok_head); + free_tokens(tok); + return((token_t*)NULL); + case '[' : + tok->operator = OPEN_SQUARE_BRACKET;; + break; + + case ']' : + tok->operator = CLOSE_SQUARE_BRACKET;; + break; + + default: { + + char *s; + int len; + + /* See if the last token is a RIGHT_ARROW + * or a DOT. If it is, then this token must + * be the name of a struct/union member. + */ + if (tok_last && + ((tok_last->operator == RIGHT_ARROW) || + (tok_last->operator == DOT))) { + tok->type = MEMBER; + } else if (process_text(&cp, tok)) { + free_tokens(tok_head); + free_tokens(tok); + return((token_t*)NULL); + } + if (tok->type == TEXT) { + return(tok); + } else if (tok->type == STRING) { + if (is_typestr(tok->string)) { + tok->type = TYPE_DEF; + } else { + tok->operator = TEXT; + return(tok); + } + break; + } else if (tok->type == CHARACTER) { + break; + } + + /* Check and See if the entire string is + * a typename (valid only for whatis case). + */ + s = strpbrk(cp, + ".\t+-*/()[]|~!$&%^<>?:&=^\"\'"); + if (!s && !tok->type && is_typestr(cp)) { + tok->type = TYPE_DEF; + len = strlen(cp) + 1; + tok->string = (char *) + kl_alloc_block(len); + memcpy(tok->string, cp, len - 1); + tok->string[len - 1] = 0; + cp = (char *)((uaddr_t)cp + len - 2); + break; + } + + /* Now check for everything else + */ + if ((s = strpbrk(cp, + " .\t+-*/()[]|~!$&%^<>?:&=^\"\'"))) { + len = (uaddr_t)s - (uaddr_t)cp + 1; + } else { + len = strlen(cp) + 1; + } + + tok->string = + (char *)kl_alloc_block(len); + memcpy(tok->string, cp, len - 1); + tok->string[len - 1] = 0; + + cp = (char *)((uaddr_t)cp + len - 2); + + /* Check to see if this is the keyword + * "sizeof". If not, then check to see if + * the string is a member name. + */ + if (!strcmp(tok->string, "sizeof")) { + tok->operator = SIZEOF; + kl_free_block((void *)tok->string); + tok->string = 0; + } else if (tok_last && + ((tok_last->operator == RIGHT_ARROW) || + (tok_last->operator == DOT))) { + tok->type = MEMBER; + } else { + tok->type = STRING; + } + break; + } + } + if (!(tok->type)) { + tok->type = OPERATOR; + } + if (!tok_head) { + tok_head = tok_last = tok; + } else { + tok_last->next = tok; + tok_last = tok; + } + cp++; + } + if (paren_count < 0) { + set_eval_error(E_CLOSE_PAREN); + error_token = tok->ptr; + free_tokens(tok_head); + return((token_t*)NULL); + } else if (paren_count > 0) { + set_eval_error(E_OPEN_PAREN); + error_token = tok->ptr; + free_tokens(tok_head); + return((token_t*)NULL); + } + return(tok_head); +} + +/* + * valid_binary_args() + */ +int +valid_binary_args(node_t *np, node_t *left, node_t *right) +{ + int op = np->operator; + + if ((op == RIGHT_ARROW) || (op == DOT)) { + if (!left) { + set_eval_error(E_MISSING_STRUCTURE); + error_token = np->tok_ptr; + return(0); + } else if (!(left->node_type == TYPE_DEF) && + !(left->node_type == MEMBER) && + !(left->operator == CLOSE_PAREN) && + !(left->operator == CLOSE_SQUARE_BRACKET)) { + set_eval_error(E_BAD_STRUCTURE); + error_token = left->tok_ptr; + return(0); + } + if (!right || (!(right->node_type == MEMBER))) { + set_eval_error(E_BAD_MEMBER); + error_token = np->tok_ptr; + return(0); + } + return(1); + } + if (!left || !right) { + set_eval_error(E_MISSING_OPERAND); + error_token = np->tok_ptr; + return(0); + } + switch (left->operator) { + case CLOSE_PAREN: + case CLOSE_SQUARE_BRACKET: + break; + default: + switch(left->node_type) { + case NUMBER: + case STRING: + case TEXT: + case CHARACTER: + case EVAL_VAR: + case MEMBER: + break; + default: + set_eval_error(E_BAD_OPERAND); + error_token = np->tok_ptr; + return(0); + } + } + switch (right->operator) { + case OPEN_PAREN: + break; + default: + switch(right->node_type) { + case NUMBER: + case STRING: + case TEXT: + case CHARACTER: + case EVAL_VAR: + case MEMBER: + break; + default: + set_eval_error(E_BAD_OPERAND); + error_token = np->tok_ptr; + return(0); + } + } + return(1); +} + +/* + * get_node_list() + */ +static node_t * +get_node_list(token_t *tp, int flags) +{ + node_t *root = (node_t *)NULL; + node_t *np = (node_t *)NULL; + node_t *last = (node_t *)NULL; + + /* Loop through the tokens and convert them to nodes. + */ + while (tp) { + np = make_node(tp, flags); + if (eval_error) { + return((node_t *)NULL); + } + if (root) { + last->next = np; + last = np; + } else { + root = last = np; + } + tp = tp->next; + } + last->next = (node_t *)NULL; /* cpw patch */ + last = (node_t *)NULL; + for (np = root; np; np = np->next) { + if (is_binary(np->operator)) { + if (!valid_binary_args(np, last, np->next)) { + free_nodelist(root); + return((node_t *)NULL); + } + } + last = np; + } + return(root); +} + +/* + * next_node() + */ +static node_t * +next_node(void) +{ + node_t *np; + if ((np = node_list)) { + node_list = node_list->next; + np->next = (node_t*)NULL; + } + return(np); +} + +/* + * eval_unary() + */ +static node_t * +eval_unary(node_t *curnp, int flags) +{ + node_t *n0, *n1; + + n0 = curnp; + + /* Peek ahead and make sure there is a next node. + * Also check to see if the next node requires + * a recursive call to do_eval(). If it does, we'll + * let the do_eval() call take care of pulling it + * off the list. + */ + if (!node_list) { + set_eval_error(E_SYNTAX_ERROR); + error_token = n0->tok_ptr; + free_nodes(n0); + return((node_t*)NULL); + } + if (n0->operator == CAST) { + if (node_list->operator == CLOSE_PAREN) { + + /* Free the CLOSE_PAREN and return + */ + free_node(next_node()); + return(n0); + } + if (!(node_list->node_type == NUMBER) && + !(node_list->node_type == VADDR) && + !((node_list->operator == ADDRESS) || + (node_list->operator == CAST) || + (node_list->operator == UNARY_MINUS) || + (node_list->operator == UNARY_PLUS) || + (node_list->operator == INDIRECTION) || + (node_list->operator == OPEN_PAREN))) { + set_eval_error(E_SYNTAX_ERROR); + error_token = node_list->tok_ptr; + free_nodes(n0); + return((node_t*)NULL); + } + } + if ((n0->operator == INDIRECTION) || + (n0->operator == ADDRESS) || + (n0->operator == OPEN_PAREN) || + is_unary(node_list->operator)) { + n1 = do_eval(flags); + if (eval_error) { + free_nodes(n0); + free_nodes(n1); + return((node_t*)NULL); + } + } else { + n1 = next_node(); + } + + if (n1->operator == OPEN_PAREN) { + /* Get the value contained within the parenthesis. + * If there was an error, just return. + */ + free_node(n1); + n1 = do_eval(flags); + if (eval_error) { + free_nodes(n1); + free_nodes(n0); + return((node_t*)NULL); + } + } + + n0->right = n1; + if (replace_unary(n0, flags) == -1) { + if (!eval_error) { + set_eval_error(E_SYNTAX_ERROR); + error_token = n0->tok_ptr; + } + free_nodes(n0); + return((node_t*)NULL); + } + return(n0); +} + +/* + * do_eval() -- Reduces an equation to a single value. + * + * Any parenthesis (and nested parenthesis) within the equation will + * be solved first via recursive calls to do_eval(). + */ +static node_t * +do_eval(int flags) +{ + node_t *root = (node_t*)NULL, *curnp, *n0, *n1; + + /* Loop through the list of nodes until we run out of nodes + * or we hit a CLOSE_PAREN. If we hit an OPEN_PAREN, make a + * recursive call to do_eval(). + */ + curnp = next_node(); + while (curnp) { + n0 = n1 = (node_t *)NULL; + + if (curnp->operator == OPEN_PAREN) { + /* Get the value contained within the parenthesis. + * If there was an error, just return. + */ + free_node(curnp); + n0 = do_eval(flags); + if (eval_error) { + free_nodes(n0); + free_nodes(root); + return((node_t *)NULL); + } + + } else if (curnp->operator == SIZEOF) { + /* Free the SIZEOF node and then make a call + * to the get_sizeof() function (which will + * get the next node off the list). + */ + n0 = get_sizeof(); + if (eval_error) { + if (!error_token) { + error_token = curnp->tok_ptr; + } + free_node(curnp); + free_nodes(root); + return((node_t *)NULL); + } + free_node(curnp); + curnp = (node_t *)NULL; + } else if (is_unary(curnp->operator)) { + n0 = eval_unary(curnp, flags); + } else { + n0 = curnp; + curnp = (node_t *)NULL; + } + if (eval_error) { + free_nodes(n0); + free_nodes(root); + return((node_t *)NULL); + } + + /* n0 should now contain a non-operator node. Check to see if + * there is a next token. If there isn't, just add the last + * rchild and return. + */ + if (!node_list) { + if (root) { + add_rchild(root, n0); + } else { + root = n0; + } + replace(root, flags); + if (eval_error) { + free_nodes(root); + return((node_t *)NULL); + } + return(root); + } + + /* Make sure the next token is an operator. + */ + if (!node_list->operator) { + free_nodes(root); + free_node(n0); + set_eval_error(E_SYNTAX_ERROR); + error_token = node_list->tok_ptr; + return((node_t *)NULL); + } else if ((node_list->operator == CLOSE_PAREN) || + (node_list->operator == CLOSE_SQUARE_BRACKET)) { + + if (root) { + add_rchild(root, n0); + } else { + root = n0; + } + + /* Reduce the resulting tree to a single value + */ + replace(root, flags); + if (eval_error) { + free_nodes(root); + return((node_t *)NULL); + } + + /* Step over the CLOSE_PAREN or CLOSE_SQUARE_BRACKET + * and then return. + */ + free_node(next_node()); + return(root); + } else if (node_list->operator == OPEN_SQUARE_BRACKET) { +next_dimension1: + /* skip over the OPEN_SQUARE_BRACKET token + */ + free_node(next_node()); + + /* Get the value contained within the brackets. This + * value must represent an array index (value or + * equation). + */ + n1 = do_eval(0); + if (eval_error) { + free_nodes(root); + free_node(n0); + free_node(n1); + return((node_t *)NULL); + } + + /* Convert the array (or pointer type) to an + * element type using the index value obtained + * above. Make sure that n0 contains some sort + * of type definition first, however. + */ + if (n0->node_type != TYPE_DEF) { + set_eval_error(E_BAD_TYPE); + error_token = n0->tok_ptr; + free_nodes(n0); + free_nodes(n1); + free_nodes(root); + return((node_t *)NULL); + } + array_to_element(n0, n1); + free_node(n1); + if (eval_error) { + free_nodes(root); + free_nodes(n0); + return((node_t *)NULL); + } + + /* If there aren't any more nodes, just + * return. + */ + if (!node_list) { + return(n0); + } + if (node_list->operator == OPEN_SQUARE_BRACKET) { + goto next_dimension1; + } + } else if (!is_binary(node_list->operator)) { + set_eval_error(E_BAD_OPERATOR); + error_token = node_list->tok_ptr; + free_nodes(root); + free_nodes(n0); + return((node_t *)NULL); + } + + /* Now get the operator node + */ + if (!(n1 = next_node())) { + set_eval_error(E_SYNTAX_ERROR); + error_token = n0->tok_ptr; + free_nodes(n0); + free_nodes(root); + return((node_t *)NULL); + } + + /* Check to see if this binary operator is RIGHT_ARROW or DOT. + * If it is, we need to reduce it to a single value node now. + */ + while ((n1->operator == RIGHT_ARROW) || (n1->operator == DOT)) { + + /* The next node must contain the name of the + * struct|union member. + */ + if (!node_list || (node_list->node_type != MEMBER)) { + set_eval_error(E_BAD_MEMBER); + error_token = n1->tok_ptr; + free_nodes(n0); + free_nodes(n1); + free_nodes(root); + return((node_t *)NULL); + } + n1->left = n0; + + /* Now get the next node and link it as the + * right child. + */ + if (!(n0 = next_node())) { + set_eval_error(E_SYNTAX_ERROR); + error_token = n1->tok_ptr; + free_nodes(n1); + free_nodes(root); + return((node_t *)NULL); + } + n1->right = n0; + if (!(n0 = replace(n1, flags))) { + if (!(eval_error)) { + set_eval_error(E_SYNTAX_ERROR); + error_token = n1->tok_ptr; + } + free_nodes(n1); + free_nodes(root); + return((node_t *)NULL); + } + n1 = (node_t *)NULL; + + /* Check to see if there is a next node. If there + * is, check to see if it is the operator CLOSE_PAREN. + * If it is, then return (skipping over the + * CLOSE_PAREN first). + */ + if (node_list && ((node_list->operator == CLOSE_PAREN) + || (node_list->operator == + CLOSE_SQUARE_BRACKET))) { + if (root) { + add_rchild(root, n0); + } else { + root = n0; + } + + /* Reduce the resulting tree to a single + * value + */ + replace(root, flags); + if (eval_error) { + free_nodes(root); + return((node_t *)NULL); + } + + /* Advance the token pointer past the + * CLOSE_PAREN and then return. + */ + free_node(next_node()); + return(root); + } + + /* Check to see if the next node is an + * OPEN_SQUARE_BRACKET. If it is, then we have to + * reduce the contents of the square brackets to + * an index array. + */ + if (node_list && (node_list->operator + == OPEN_SQUARE_BRACKET)) { + + /* Advance the token pointer and call + * do_eval() again. + */ + free_node(next_node()); +next_dimension2: + n1 = do_eval(0); + if (eval_error) { + free_node(n0); + free_node(n1); + free_nodes(root); + return((node_t *)NULL); + } + + /* Convert the array (or pointer type) to + * an element type using the index value + * obtained above. Make sure that n0 + * contains some sort of type definition + * first, however. + */ + if (n0->node_type != TYPE_DEF) { + set_eval_error(E_BAD_TYPE); + error_token = n0->tok_ptr; + free_node(n0); + free_node(n1); + free_node(root); + return((node_t *)NULL); + } + array_to_element(n0, n1); + free_node(n1); + if (eval_error) { + free_node(n0); + free_node(root); + return((node_t *)NULL); + } + } + + /* Now get the next operator node (if there is one). + */ + if (!node_list) { + if (root) { + add_rchild(root, n0); + } else { + root = n0; + } + return(root); + } + n1 = next_node(); + if (n1->operator == OPEN_SQUARE_BRACKET) { + goto next_dimension2; + } + } + + if (n1 && ((n1->operator == CLOSE_PAREN) || + (n1->operator == CLOSE_SQUARE_BRACKET))) { + free_node(n1); + if (root) { + add_rchild(root, n0); + } else { + root = n0; + } + replace(root, flags); + if (eval_error) { + free_nodes(root); + return((node_t *)NULL); + } + return(root); + } + + if (!root) { + root = n1; + n1->left = n0; + } else if (precedence(root->operator) + >= precedence(n1->operator)) { + add_rchild(root, n0); + n1->left = root; + root = n1; + } else { + if (!root->right) { + n1->left = n0; + root->right = n1; + } else { + add_rchild(root, n0); + n1->left = root->right; + root->right = n1; + } + } + curnp = next_node(); + } /* while(curnp) */ + return(root); +} + +/* + * is_unary() + */ +static int +is_unary(int op) +{ + switch (op) { + case LOGICAL_NEGATION : + case ADDRESS : + case INDIRECTION : + case UNARY_MINUS : + case UNARY_PLUS : + case ONES_COMPLEMENT : + case CAST : + return(1); + + default : + return(0); + } +} + + +/* + * is_binary() + */ +static int +is_binary(int op) +{ + switch (op) { + + case BITWISE_OR : + case BITWISE_EXCLUSIVE_OR : + case BITWISE_AND : + case RIGHT_SHIFT : + case LEFT_SHIFT : + case ADD : + case SUBTRACT : + case MULTIPLY : + case DIVIDE : + case MODULUS : + case LOGICAL_OR : + case LOGICAL_AND : + case EQUAL : + case NOT_EQUAL : + case LESS_THAN : + case GREATER_THAN : + case LESS_THAN_OR_EQUAL : + case GREATER_THAN_OR_EQUAL : + case RIGHT_ARROW : + case DOT : + return(1); + + default : + return(0); + } +} + +/* + * precedence() + */ +static int +precedence(int a) +{ + if ((a >= CONDITIONAL) && (a <= CONDITIONAL_ELSE)) { + return(1); + } else if (a == LOGICAL_OR) { + return(2); + } else if (a == LOGICAL_AND) { + return(3); + } else if (a == BITWISE_OR) { + return(4); + } else if (a == BITWISE_EXCLUSIVE_OR) { + return(5); + } else if (a == BITWISE_AND) { + return(6); + } else if ((a >= EQUAL) && (a <= NOT_EQUAL)) { + return(7); + } else if ((a >= LESS_THAN) && (a <= GREATER_THAN_OR_EQUAL)) { + return(8); + } else if ((a >= RIGHT_SHIFT) && (a <= LEFT_SHIFT)) { + return(9); + } else if ((a >= ADD) && (a <= SUBTRACT)) { + return(10); + } else if ((a >= MULTIPLY) && (a <= MODULUS)) { + return(11); + } else if ((a >= LOGICAL_NEGATION) && (a <= SIZEOF)) { + return(12); + } else if ((a >= RIGHT_ARROW) && (a <= DOT)) { + return(13); + } else { + return(0); + } +} + +/* + * esc_char() + */ +char +esc_char(char *str) +{ + long int val; + unsigned long uval; + char ch; + + if (strlen(str) > 1) { + uval = kl_strtoull(str, (char **)NULL, 8); + val = uval; + ch = (char)val; + } else { + ch = str[0]; + } + switch (ch) { + case 'a' : + return((char)7); + case 'b' : + return((char)8); + case 't' : + return((char)9); + case 'n' : + return((char)10); + case 'f' : + return((char)12); + case 'r' : + return((char)13); + case 'e' : + return((char)27); + default: + return(ch); + } +} + +/* + * make_node() + */ +static node_t * +make_node(token_t *t, int flags) +{ + node_t *np; + + set_eval_error(0); + np = (node_t*)kl_alloc_block(sizeof(*np)); + + if (t->type == OPERATOR) { + + /* Check to see if this token represents a typecast + */ + if (t->operator == CAST) { + type_t *tp; + + if (!(np->type = get_type(t->string, flags))) { + set_eval_error(E_BAD_CAST); + error_token = t->ptr; + free_nodes(np); + return((node_t*)NULL); + } + + /* Determin if this is a pointer to a type + */ + tp = np->type; + if (tp->flag == POINTER_FLAG) { + np->flags = POINTER_FLAG; + tp = tp->t_next; + while (tp->flag == POINTER_FLAG) { + tp = tp->t_next; + } + } + switch(tp->flag) { + case KLTYPE_FLAG: + np->flags |= KLTYPE_FLAG; + break; + + default: + free_nodes(np); + set_eval_error(E_BAD_CAST); + error_token = t->ptr; + return((node_t*)NULL); + } + if (!t->next) { + if (flags & C_WHATIS) { + np->node_type = TYPE_DEF; + } else { + set_eval_error(E_BAD_CAST); + error_token = t->ptr; + return((node_t*)NULL); + } + } else { + np->node_type = OPERATOR; + np->operator = CAST; + } + } else { + np->node_type = OPERATOR; + np->operator = t->operator; + } + } else if (t->type == MEMBER) { + np->name = (char *)dup_block((void *)t->string, strlen(t->string)+1); + np->node_type = MEMBER; + } else if ((t->type == STRING) || (t->type == TYPE_DEF)) { + syment_t *sp; + dbg_sym_t *stp; + dbg_type_t *sttp; + + if ((sp = kl_lkup_symname(t->string))) { + if (!(flags & C_NOVARS)) { + int has_type = 0; + + /* The string is a symbol name. We'll treat it as + * a global kernel variable and, at least, gather in + * the address of the symbol and the value it points + * to. + */ + np->address = sp->s_addr; + np->flags |= ADDRESS_FLAG; + np->name = t->string; + t->string = (char*)NULL; + + /* Need to see if there is type information available + * for this variable. Since this mapping is not + * available yet, we will just attach a type struct + * for either uint32_t or uint64_t (depending on the + * size of a kernel pointer). That will at least let + * us do something and will prevent the scenario where + * we have a type node with out a pointer to a type + * struct! + */ + np->node_type = TYPE_DEF; + np->flags |= KLTYPE_FLAG; + np->value = *((kaddr_t *)np->address); + /* try to get the actual type info for the variable */ + if(((stp = dbg_find_sym(sp->s_name, DBG_VAR, + (uint64_t)0)) != NULL)){ + if((sttp = (dbg_type_t *) + kl_find_typenum(stp->sym_typenum)) + != NULL){ + /* kl_get_typestring(sttp); */ + has_type = 1; + if(sttp->st_klt.kl_type == KLT_POINTER){ + np->flags ^= KLTYPE_FLAG; + np->flags |= POINTER_FLAG; + np->type = + get_type(sttp->st_typestr, + flags); + } else { + np->type = + kl_alloc_block(sizeof(type_t)); + np->type->un.kltp = + &sttp->st_klt; + } + } + } + /* no type info for the variable found */ + if(!has_type){ + if (ptrsz64) { + np->type = get_type("uint64_t", flags); + } else { + np->type = get_type("uint32_t", flags); + } + } + } + kl_free_block((void *)sp); + } else if (flags & (C_WHATIS|C_SIZEOF)) { + + kltype_t *kltp; + + if ((kltp = kl_find_type(t->string, KLT_TYPES))) { + + np->node_type = TYPE_DEF; + np->flags = KLTYPE_FLAG; + np->type = (type_t*) + kl_alloc_block(sizeof(type_t)); + np->type->flag = KLTYPE_FLAG; + np->type->t_kltp = kltp; + } else { + if (get_value(t->string, + (uint64_t *)&np->value)) { + set_eval_error(E_BAD_VALUE); + error_token = t->ptr; + free_nodes(np); + return((node_t*)NULL); + } + if (!strncmp(t->string, "0x", 2) || + !strncmp(t->string, "0X", 2)) { + np->flags |= UNSIGNED_FLAG; + } + np->node_type = NUMBER; + } + np->tok_ptr = t->ptr; + return(np); + } else { + if (get_value(t->string, (uint64_t *)&np->value)) { + set_eval_error(E_BAD_VALUE); + error_token = t->ptr; + free_nodes(np); + return((node_t*)NULL); + } + if (np->value > 0xffffffff) { + np->byte_size = 8; + } else { + np->byte_size = 4; + } + if (!strncmp(t->string, "0x", 2) || + !strncmp(t->string, "0X", 2)) { + np->flags |= UNSIGNED_FLAG; + } + np->node_type = NUMBER; + } + } else if (t->type == CHARACTER) { + char *cp; + + /* Step over the single quote + */ + cp = (t->ptr + 1); + if (*cp == '\\') { + int i = 0; + char str[16]; + + /* Step over the back slash + */ + cp++; + while (*cp != '\'') { + str[i++] = *cp++; + } + str[i] = 0; + np->value = esc_char(str); + } else { + np->value = *cp; + } + np->type = get_type("char", flags); + np->node_type = TYPE_DEF; + np->flags |= KLTYPE_FLAG; + } else if (t->type == TEXT) { + np->node_type = TEXT; + np->name = t->string; + /* So the block doesn't get freed twice */ + t->string = (char*)NULL; + } else { + set_eval_error(E_SYNTAX_ERROR); + error_token = t->ptr; + return((node_t*)NULL); + } + np->tok_ptr = t->ptr; + return(np); +} + +/* + * add_node() + */ +static int +add_node(node_t *root, node_t *new_node) +{ + node_t *n = root; + + /* Find the most lower-right node + */ + while (n->right) { + n = n->right; + } + + /* If the node we found is a leaf node, return an error (we will + * have to insert the node instead). + */ + if (n->node_type == NUMBER) { + return(-1); + } else { + n->right = new_node; + } + return(0); +} + +/* + * add_rchild() + */ +static int +add_rchild(node_t *root, node_t *new_node) +{ + if (add_node(root, new_node) == -1) { + return(-1); + } + return(0); +} + +/* + * free_type() + */ +static void +free_type(type_t *head) +{ + type_t *t0, *t1; + + t0 = head; + while(t0) { + if (t0->flag == POINTER_FLAG) { + t1 = t0->t_next; + kl_free_block((void *)t0); + t0 = t1; + } else { + if (t0->flag != KLTYPE_FLAG) { + kl_free_block((void *)t0->t_kltp); + } + kl_free_block((void *)t0); + t0 = (type_t *)NULL; + } + } + return; +} + +/* + * get_type() -- Convert a typecast string into a type. + * + * Returns a pointer to a struct containing type information. + * The type of struct returned is indicated by the contents + * of type. If the typecast contains an asterisk, set ptr_type + * equal to one, otherwise set it equal to zero. + */ +static type_t * +get_type(char *s, int flags) +{ + int len, type = 0; + char *cp, typename[128]; + type_t *t, *head, *last; + kltype_t *kltp; + + head = last = (type_t *)NULL; + + /* Get the type string + */ + if (!strncmp(s, "struct", 6)) { + if ((cp = strpbrk(s + 7, " \t*"))) { + len = cp - (s + 7); + } else { + len = strlen(s + 7); + } + memcpy(typename, s + 7, len); + } else if (!strncmp(s, "union", 5)) { + if ((cp = strpbrk(s + 6, " \t*"))) { + len = cp - (s + 6); + } else { + len = strlen(s + 6); + } + memcpy(typename, s + 6, len); + } else { + if ((cp = strpbrk(s, "*)"))) { + len = cp - s; + } else { + len = strlen(s); + } + memcpy(typename, s, len); + } + + /* Strip off any trailing spaces + */ + while (len && ((typename[len - 1] == ' ') || + (typename[len - 1] == '\t'))) { + len--; + } + typename[len] = 0; + + if (!(kltp = kl_find_type(typename, KLT_TYPES))) { + return ((type_t *)NULL); + } + type = KLTYPE_FLAG; + + /* check to see if this cast is a pointer to a type, a pointer + * to a pointer to a type, etc. + */ + cp = s; + while ((cp = strpbrk(cp, "*"))) { + t = (type_t *)kl_alloc_block(sizeof(type_t)); + t->flag = POINTER_FLAG; + if (last) { + last->t_next = t; + last = t; + } else { + head = last = t; + } + cp++; + } + + /* Allocate a type block that will point to the type specific + * record. + */ + t = (type_t *)kl_alloc_block(sizeof(type_t)); + t->flag = type; + + switch (t->flag) { + + case KLTYPE_FLAG: + t->t_kltp = kltp; + break; + + default: + free_type(head); + return((type_t*)NULL); + } + if (last) { + last->t_next = t; + } else { + head = t; + } + return(head); +} + +/* + * free_node() + */ +static void +free_node(node_t *np) +{ + /* If there is nothing to free, just return. + */ + if (!np) { + return; + } + if (np->name) { + kl_free_block((void *)np->name); + } + free_type(np->type); + kl_free_block((void *)np); +} + +/* + * free_nodes() + */ +void +free_nodes(node_t *np) +{ + node_t *q; + + /* If there is nothing to free, just return. + */ + if (!np) { + return; + } + if ((q = np->left)) { + free_nodes(q); + } + if ((q = np->right)) { + free_nodes(q); + } + if (np->name) { + kl_free_block((void *)np->name); + } + free_type(np->type); + kl_free_block((void *)np); +} + +/* + * free_nodelist() + */ +static void +free_nodelist(node_t *np) +{ + node_t *nnp; + + while(np) { + nnp = np->next; + free_node(np); + np = nnp; + } +} + +extern int alloc_debug; + +/* + * free_eval_memory() + */ +void +free_eval_memory(void) +{ + free_nodelist(node_list); + node_list = (node_t*)NULL; +} + +/* + * get_sizeof() + */ +static node_t * +get_sizeof() +{ + node_t *curnp, *n0 = NULL; + + if (!(curnp = next_node())) { + set_eval_error(E_SYNTAX_ERROR); + return((node_t*)NULL); + } + + /* The next token should be a CAST or an open paren. + * If it's something else, then return an error. + */ + if (curnp->operator == OPEN_PAREN) { + free_nodes(curnp); + n0 = do_eval(C_SIZEOF); + if (eval_error) { + error_token = n0->tok_ptr; + free_nodes(n0); + return((node_t*)NULL); + } + } else if (curnp->operator == CAST) { + n0 = curnp; + } else { + set_eval_error(E_BAD_TYPE); + error_token = n0->tok_ptr; + free_nodes(n0); + return((node_t*)NULL); + } + + if (!n0->type) { + set_eval_error(E_NOTYPE); + error_token = n0->tok_ptr; + free_nodes(n0); + return((node_t*)NULL); + } + + if (n0->type->flag & POINTER_FLAG) { + n0->value = sizeof(void *); + } else if (n0->type->flag & KLTYPE_FLAG) { + kltype_t *kltp; + + kltp = kl_realtype(n0->type->t_kltp, 0); + + if (kltp->kl_bit_size) { + n0->value = kltp->kl_bit_size / 8; + if (kltp->kl_bit_size % 8) { + n0->value += 1; + } + } else { + n0->value = kltp->kl_size; + } + } else { + set_eval_error(E_BAD_TYPE); + error_token = n0->tok_ptr; + free_nodes(n0); + return((node_t*)NULL); + } + n0->node_type = NUMBER; + n0->flags = 0; + n0->operator = 0; + n0->byte_size = 0; + n0->address = 0; + if (n0->type) { + free_type(n0->type); + n0->type = 0; + } + return(n0); +} + +/* + * apply_unary() + */ +static int +apply_unary(node_t *n, uint64_t *value) +{ + if (!n || !n->right) { + return(-1); + } + + switch (n->operator) { + + case UNARY_MINUS : + *value = (0 - n->right->value); + break; + + case UNARY_PLUS : + *value = (n->right->value); + break; + + case ONES_COMPLEMENT : + *value = ~(n->right->value); + break; + + case LOGICAL_NEGATION : + if (n->right->value) { + *value = 0; + } else { + *value = 1; + } + logical_flag++; + break; + + default : + break; + } + return(0); +} + +/* + * pointer_math() + */ +static int +pointer_math(node_t *np, uint64_t *value, int type, int flags) +{ + int size; + uint64_t lvalue, rvalue; + type_t *tp = NULL, *tp1; + + if (type < 0) { + if (np->left->flags & POINTER_FLAG) { + + /* Since we only allow pointer math, + * anything other than a pointer causes + * failure. + */ + tp = (type_t*)np->left->type; + if (tp->flag != POINTER_FLAG) { + set_eval_error(E_SYNTAX_ERROR); + error_token = np->left->tok_ptr; + return(-1); + } + + tp = tp->t_next; + + switch (tp->flag) { + + case POINTER_FLAG : + size = sizeof(void *); + break; + + case KLTYPE_FLAG : { + /* Get the size of the real type, + * not just the size of a pointer + * If there isn't any type info, + * then just set size equal to the + * size of a pointer. + */ + kltype_t *kltp, *rkltp; + + kltp = tp->t_kltp; + rkltp = kl_realtype(kltp, 0); + if (!(size = rkltp->kl_size)) { + if (kltp != rkltp) { + size = kltp->kl_size; + } else { + size = sizeof(void *); + } + } + break; + } + + default : + set_eval_error(E_SYNTAX_ERROR); + error_token = np->left->tok_ptr; + return(-1); + } + lvalue = np->left->value; + } else { + size = sizeof(void *); + lvalue = np->left->address; + } + switch (np->operator) { + case ADD : + *value = lvalue + (np->right->value * size); + break; + + case SUBTRACT : + *value = lvalue - (np->right->value * size); + break; + + default : + set_eval_error(E_BAD_OPERATOR); + error_token = np->tok_ptr; + return(-1); + } + } else if (type > 0) { + if (np->right->flags & POINTER_FLAG) { + + /* Since we only allow pointer math, + * anything other than a pointer causes + * failure. + */ + tp = (type_t*)np->right->type; + if (tp->flag != POINTER_FLAG) { + set_eval_error(E_SYNTAX_ERROR); + error_token = np->right->tok_ptr; + return(-1); + } + + tp = tp->t_next; + + switch (tp->flag) { + + case POINTER_FLAG : + size = sizeof(void *); + break; + + case KLTYPE_FLAG : + size = tp->t_kltp->kl_size; + break; + + default : + set_eval_error(E_SYNTAX_ERROR); + error_token = np->right->tok_ptr; + return(-1); + } + rvalue = np->right->value; + } else { + size = sizeof(void *); + rvalue = np->right->address; + } + switch (np->operator) { + case ADD : + *value = rvalue + (np->left->value * size); + break; + + case SUBTRACT : + *value = rvalue - (np->left->value * size); + break; + + default : + set_eval_error(E_BAD_OPERATOR); + error_token = np->tok_ptr; + return(-1); + } + } else { + return(-1); + } + tp1 = (type_t *)kl_alloc_block(sizeof(type_t)); + tp1->flag = POINTER_FLAG; + np->type = tp1; + while (tp->flag == POINTER_FLAG) { + tp1->t_next = (type_t *)kl_alloc_block(sizeof(type_t)); + tp1->flag = POINTER_FLAG; + tp1 = tp1->t_next; + tp = tp->t_next; + } + if (tp) { + tp1->t_next = (type_t *)kl_alloc_block(sizeof(type_t)); + tp1 = tp1->t_next; + tp1->flag = KLTYPE_FLAG; + tp1->t_kltp = tp->t_kltp; + if (type < 0) { + if (np->left->flags & POINTER_FLAG) { + np->flags |= POINTER_FLAG; + } else { + np->flags |= VADDR; + } + } else { + if (np->right->flags & POINTER_FLAG) { + np->flags |= POINTER_FLAG; + } else { + np->flags |= VADDR; + } + } + } + return(0); +} + +/* + * check_unsigned() + */ +int +check_unsigned(node_t *np) +{ + kltype_t *kltp, *rkltp; + + if (np->flags & UNSIGNED_FLAG) { + return(1); + } + if (!np->type) { + return(0); + } + if (np->type->flag == POINTER_FLAG) { + return(0); + } + kltp = np->type->t_kltp; + if ((rkltp = kl_realtype(kltp, 0))) { + if (rkltp->kl_encoding == ENC_UNSIGNED) { + np->flags |= UNSIGNED_FLAG; + return(1); + } + } + return(0); +} + +/* + * apply() + */ +static int +apply(node_t *np, uint64_t *value, int flags) +{ + int ltype, rtype, do_signed = 0; + + /* There must be two operands + */ + if (!np->right || !np->left) { + set_eval_error(E_MISSING_OPERAND); + error_token = np->tok_ptr; + return(-1); + } + + if (np->right->node_type == OPERATOR) { + replace(np->right, flags); + if (eval_error) { + return(-1); + } + } + + ltype = np->left->node_type; + rtype = np->right->node_type; + if ((ltype == TYPE_DEF) || (ltype == VADDR)) { + if ((rtype == TYPE_DEF) || (rtype == VADDR)) { + set_eval_error(E_NO_VALUE); + error_token = np->tok_ptr; + return(-1); + } + if (check_unsigned(np->left)) { + np->flags |= UNSIGNED_FLAG; + } else { + do_signed++; + } + if (!type_to_number(np->left)) { + return(pointer_math(np, value, -1, flags)); + } + np->byte_size = np->left->byte_size; + } else if ((rtype == TYPE_DEF) || (rtype == VADDR)) { + if ((ltype == TYPE_DEF) || (ltype == VADDR)) { + error_token = np->tok_ptr; + set_eval_error(E_NO_VALUE); + return(-1); + } + if (check_unsigned(np->right)) { + np->flags |= UNSIGNED_FLAG; + } else { + do_signed++; + } + if (!type_to_number(np->right)) { + return(pointer_math(np, value, 1, flags)); + } + np->byte_size = np->right->byte_size; + } else if ((np->left->flags & UNSIGNED_FLAG) || + (np->right->flags & UNSIGNED_FLAG)) { + np->flags |= UNSIGNED_FLAG; + } else { + do_signed++; + } + + if (do_signed) { + switch (np->operator) { + case ADD : + *value = (int64_t)np->left->value + + (int64_t)np->right->value; + break; + + case SUBTRACT : + *value = (int64_t)np->left->value - + (int64_t)np->right->value; + break; + + case MULTIPLY : + *value = (int64_t)np->left->value * + (int64_t)np->right->value; + break; + + case DIVIDE : + if ((int64_t)np->right->value == 0) { + set_eval_error(E_DIVIDE_BY_ZERO); + error_token = np->right->tok_ptr; + return(-1); + } + *value = (int64_t)np->left->value / + (int64_t)np->right->value; + break; + + case BITWISE_OR : + *value = (int64_t)np->left->value | + (int64_t)np->right->value; + break; + + case BITWISE_AND : + *value = (int64_t)np->left->value & + (int64_t)np->right->value; + break; + + case MODULUS : + if ((int64_t)np->right->value == 0) { + set_eval_error(E_DIVIDE_BY_ZERO); + error_token = np->right->tok_ptr; + return(-1); + } + *value = (int64_t)np->left->value % + (int64_t)np->right->value; + break; + + case RIGHT_SHIFT : + *value = + (int64_t)np->left->value >> + (int64_t)np->right->value; + break; + + case LEFT_SHIFT : + *value = + (int64_t)np->left->value << + (int64_t)np->right->value; + break; + + case LOGICAL_OR : + if ((int64_t)np->left->value || + (int64_t)np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + case LOGICAL_AND : + if ((int64_t)np->left->value && + (int64_t)np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + case EQUAL : + if ((int64_t)np->left->value == + (int64_t)np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + case NOT_EQUAL : + if ((int64_t)np->left->value != + (int64_t)np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + case LESS_THAN : + if ((int64_t)np->left->value < + (int64_t)np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + case GREATER_THAN : + if ((int64_t)np->left->value > + (int64_t)np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + case LESS_THAN_OR_EQUAL : + if ((int64_t)np->left->value <= + (int64_t)np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + case GREATER_THAN_OR_EQUAL : + if ((int64_t)np->left->value >= + (int64_t)np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + default : + break; + } + } else { + switch (np->operator) { + case ADD : + *value = np->left->value + np->right->value; + break; + + case SUBTRACT : + *value = np->left->value - np->right->value; + break; + + case MULTIPLY : + *value = np->left->value * np->right->value; + break; + + case DIVIDE : + *value = np->left->value / np->right->value; + break; + + case BITWISE_OR : + *value = np->left->value | np->right->value; + break; + + case BITWISE_AND : + *value = np->left->value & np->right->value; + break; + + case MODULUS : + *value = np->left->value % np->right->value; + break; + + case RIGHT_SHIFT : + *value = np->left->value >> np->right->value; + break; + + case LEFT_SHIFT : + *value = np->left->value << np->right->value; + break; + + case LOGICAL_OR : + if (np->left->value || np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + case LOGICAL_AND : + if (np->left->value && np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + case EQUAL : + if (np->left->value == np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + case NOT_EQUAL : + if (np->left->value != np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + case LESS_THAN : + if (np->left->value < np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + case GREATER_THAN : + if (np->left->value > np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + case LESS_THAN_OR_EQUAL : + if (np->left->value <= np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + case GREATER_THAN_OR_EQUAL : + if (np->left->value >= np->right->value) { + *value = 1; + } else { + *value = 0; + } + logical_flag++; + break; + + default : + break; + } + } + return(0); +} + +/* + * member_to_type() + */ +static type_t * +member_to_type(kltype_t *kltp, int flags) +{ + kltype_t *rkltp; + type_t *tp, *head = (type_t *)NULL, *last = (type_t *)NULL; + + /* Make sure this is a member + */ + if (kltp->kl_type != KLT_MEMBER) { + return((type_t *)NULL); + } + + rkltp = kltp->kl_realtype; + while (rkltp && rkltp->kl_type == KLT_POINTER) { + tp = (type_t *)kl_alloc_block(sizeof(type_t)); + tp->flag = POINTER_FLAG; + if (last) { + last->t_next = tp; + last = tp; + } else { + head = last = tp; + } + rkltp = rkltp->kl_realtype; + } + + /* If We step past all the pointer records and don't point + * at anything, this must be a void pointer. Setup a VOID + * type struct so that we can maintain a pointer to some + * type info. + */ + if (!rkltp) { + tp = (type_t *)kl_alloc_block(sizeof(type_t)); + tp->flag = VOID_FLAG; + tp->t_kltp = kltp; + if (last) { + last->t_next = tp; + last = tp; + } else { + head = last = tp; + } + return(head); + } + + tp = (type_t *)kl_alloc_block(sizeof(type_t)); + tp->flag = KLTYPE_FLAG; + tp->t_kltp = kltp; + if (last) { + last->t_next = tp; + } else { + head = tp; + } + return(head); +} + +/* + * replace() -- + * + * Replace the tree with a node containing the numerical result of + * the equation. If pointer math is performed, the result will have + * the same type as the pointer. + */ +static node_t * +replace(node_t *np, int flags) +{ + int offset; + uint64_t value; + node_t *q; + + if (!np) { + return((node_t *)NULL); + } + + if (np->node_type == OPERATOR) { + if (!(q = np->left)) { + return((node_t *)NULL); + } + while (q) { + if (!replace(q, flags)) { + return((node_t *)NULL); + } + q = q->right; + } + + if ((np->operator == RIGHT_ARROW) || (np->operator == DOT)) { + kaddr_t addr = 0; + type_t *tp; + + if (!have_debug_file) { + kdb_printf("no debuginfo file\n"); + return 0; + } + + /* The left node must point to a TYPE_DEF + */ + if (np->left->node_type != TYPE_DEF) { + if (np->left->flags & NOTYPE_FLAG) { + set_eval_error(E_NOTYPE); + error_token = np->left->tok_ptr; + } else { + set_eval_error(E_BAD_TYPE); + error_token = np->left->tok_ptr; + } + return((node_t *)NULL); + } + + /* Get the type information. Check to see if we + * have a pointer to a type. If we do, we need + * to strip off the pointer and get the type info. + */ + if (np->left->type->flag == POINTER_FLAG) { + tp = np->left->type->t_next; + kl_free_block((void *)np->left->type); + } else { + tp = np->left->type; + } + + /* We need to zero out the left child's type pointer + * to prevent the type structs from being prematurely + * freed (upon success). We have to remember, however, + * to the free the type information before we return. + */ + np->left->type = (type_t*)NULL; + + /* tp should now point at a type_t struct that + * references a kltype_t struct. If it points + * to anything else, return failure. + * + */ + if (tp->flag != KLTYPE_FLAG) { + set_eval_error(E_BAD_TYPE); + error_token = np->left->tok_ptr; + free_type(tp); + return((node_t *)NULL); + } + + switch (tp->flag) { + case KLTYPE_FLAG: { + /* Make sure that the type referenced + * is a struct, union, or pointer to + * a struct or union. If it isn't one + * of these, then return failure. + */ + kltype_t *kltp, *kltmp; + + kltp = kl_realtype(tp->t_kltp, 0); + if ((kltp->kl_type != KLT_STRUCT) && + (kltp->kl_type != KLT_UNION)) { + error_token = + np->left->tok_ptr; + set_eval_error(E_BAD_TYPE); + free_type(tp); + return((node_t *)NULL); + } + + /* Get type information for member. + * If member is a pointer to a type, + * get the pointer address and load + * it into value. In any event, load + * the struct/union address plus the + * offset of the member. + */ + kltmp = kl_get_member(kltp, + np->right->name); + if (!kltmp) { + set_eval_error(E_BAD_MEMBER); + error_token = + np->right->tok_ptr; + free_type(tp); + return((node_t *)NULL); + } + + /* We can't just use the offset value + * for the member. That's because it + * may be from an anonymous struct or + * union within another struct + * definition. + */ + offset = kl_get_member_offset(kltp, + np->right->name); + np->type = member_to_type(kltmp, flags); + if (!np->type) { + set_eval_error(E_BAD_MEMBER); + error_token = + np->right->tok_ptr; + free_type(tp); + return((node_t *)NULL); + } + + /* Now free the struct type information + */ + free_type(tp); + np->node_type = TYPE_DEF; + np->flags |= KLTYPE_FLAG; + np->operator = 0; + addr = 0; + if (np->left->flags & POINTER_FLAG) { + addr = np->left->value + + offset; + } else if (np->left->flags & + ADDRESS_FLAG) { + addr = np->left->address + + offset; + } + if (addr) { + np->address = addr; + np->flags |= ADDRESS_FLAG; + } + + if (np->type->flag == POINTER_FLAG) { + np->flags |= POINTER_FLAG; + np->value = *((kaddr_t *)addr); + } else { + np->value = addr; + } + break; + } + } + free_nodes(np->left); + free_nodes(np->right); + np->left = np->right = (node_t*)NULL; + return(np); + } else { + if (!np->left || !np->right) { + set_eval_error(E_MISSING_OPERAND); + error_token = np->tok_ptr; + return((node_t *)NULL); + } + if (np->left->byte_size && np->right->byte_size) { + if (np->left->byte_size > + np->right->byte_size) { + + /* Left byte_size is greater than right + */ + np->byte_size = np->left->byte_size; + np->type = np->left->type; + np->flags = np->left->flags; + free_type(np->right->type); + } else if (np->left->byte_size < + np->right->byte_size) { + + /* Right byte_size is greater than left + */ + np->byte_size = np->right->byte_size; + np->type = np->right->type; + np->flags = np->right->flags; + free_type(np->left->type); + } else { + + /* Left and right byte_size is equal + */ + if (np->left->flags & UNSIGNED_FLAG) { + np->byte_size = + np->left->byte_size; + np->type = np->left->type; + np->flags = np->left->flags; + free_type(np->right->type); + } else if (np->right->flags & + UNSIGNED_FLAG) { + np->byte_size = + np->right->byte_size; + np->type = np->right->type; + np->flags = np->right->flags; + free_type(np->left->type); + } else { + np->byte_size = + np->left->byte_size; + np->type = np->left->type; + np->flags = np->left->flags; + free_type(np->right->type); + } + } + } else if (np->left->byte_size) { + np->byte_size = np->left->byte_size; + np->type = np->left->type; + np->flags = np->left->flags; + free_type(np->right->type); + } else if (np->right->byte_size) { + np->byte_size = np->right->byte_size; + np->type = np->right->type; + np->flags = np->right->flags; + } else { + /* XXX - No byte sizes + */ + } + + if (apply(np, &value, flags)) { + return((node_t *)NULL); + } + } + np->right->type = np->left->type = (type_t*)NULL; + + /* Flesh out the rest of the node struct. + */ + if (np->type) { + np->node_type = TYPE_DEF; + np->flags |= KLTYPE_FLAG; + } else { + np->node_type = NUMBER; + np->flags &= ~(KLTYPE_FLAG); + } + np->operator = 0; + np->value = value; + kl_free_block((void *)np->left); + kl_free_block((void *)np->right); + np->left = np->right = (node_t*)NULL; + } + return(np); +} + +/* + * replace_cast() + */ +static int +replace_cast(node_t *n, int flags) +{ + type_t *t; + + if (!n) { + set_eval_error(E_SYNTAX_ERROR); + return(-1); + } else if (!n->right) { + set_eval_error(E_SYNTAX_ERROR); + error_token = n->tok_ptr; + return(-1); + } + if (n->flags & POINTER_FLAG) { + if (n->right->node_type == VADDR) { + if (n->right->flags & ADDRESS_FLAG) { + n->value = n->right->address; + } else { + set_eval_error(E_SYNTAX_ERROR); + error_token = n->right->tok_ptr; + return(-1); + } + + } else { + n->value = n->right->value; + n->address = 0; + } + } else if (n->right->flags & ADDRESS_FLAG) { + n->flags |= ADDRESS_FLAG; + n->address = n->right->address; + n->value = n->right->value; + } else { + kltype_t *kltp; + + if (!(t = eval_type(n))) { + set_eval_error(E_BAD_TYPE); + error_token = n->tok_ptr; + return(-1); + } + if (t->t_kltp->kl_type != KLT_BASE) { + + kltp = kl_realtype(t->t_kltp, 0); + if (kltp->kl_type != KLT_BASE) { + set_eval_error(E_BAD_CAST); + error_token = n->tok_ptr; + return(-1); + } + } + n->value = n->right->value; + n->type = t; + } + n->node_type = TYPE_DEF; + n->operator = 0; + free_node(n->right); + n->right = (node_t *)NULL; + return(0); +} + +/* + * replace_indirection() + */ +static int +replace_indirection(node_t *n, int flags) +{ + kaddr_t addr; + type_t *t, *tp, *rtp; + + /* Make sure there is a right child and that it is a TYPE_DEF. + */ + if (!n->right) { + set_eval_error(E_BAD_TYPE); + error_token = n->tok_ptr; + return(-1); + } else if (n->right->node_type != TYPE_DEF) { + set_eval_error(E_BAD_TYPE); + error_token = n->right->tok_ptr; + return(-1); + } + + /* Make sure the right node contains a pointer or address value. + * Note that it's possible for the whatis command to generate + * this case without any actual pointer/address value. + */ + if (!(n->right->flags & (POINTER_FLAG|ADDRESS_FLAG))) { + set_eval_error(E_BAD_POINTER); + error_token = n->right->tok_ptr; + return(-1); + } + + /* Get the pointer to the first type struct and make sure + * it's a pointer. + */ + if (!(tp = n->right->type) || (tp->flag != POINTER_FLAG)) { + set_eval_error(E_BAD_TYPE); + error_token = n->right->tok_ptr; + return(-1); + } + + /* Make sure we have a pointer to a type structure. + */ + if (!(n->right->flags & KLTYPE_FLAG)) { + set_eval_error(E_BAD_TYPE); + error_token = n->right->tok_ptr; + return(-1); + } + + n->node_type = TYPE_DEF; + n->flags = KLTYPE_FLAG; + n->operator = 0; + + if (!(t = tp->t_next)) { + set_eval_error(E_BAD_TYPE); + error_token = n->right->tok_ptr; + return(-1); + } + + if (!(rtp = eval_type(n->right))) { + set_eval_error(E_BAD_TYPE); + error_token = n->right->tok_ptr; + return(-1); + } + + /* Zero out the type field in the right child so + * it wont accidently be freed when the right child + * is freed (upon success). + */ + n->right->type = (type_t*)NULL; + + n->type = t; + + /* Free the pointer struct + */ + kl_free_block((void *)tp); + + /* Get the pointer address + */ + addr = n->address = n->right->value; + n->flags |= ADDRESS_FLAG; + + if (rtp->t_kltp->kl_type == KLT_MEMBER) { + /* If this is a member, we have to step over the KLT_MEMBER + * struct and then make sure we have a KLT_POINTER struct. + * If we do, we step over it too...otherwise return an + * error. + */ + if (rtp->t_kltp->kl_realtype->kl_type != KLT_POINTER) { + set_eval_error(E_BAD_TYPE); + error_token = n->right->tok_ptr; + return(-1); + } + rtp->t_kltp = rtp->t_kltp->kl_realtype; + } + + if (rtp->t_kltp->kl_type == KLT_POINTER) { + /* Strip off the pointer type record so that + * we pick up the actual type definition with + * our indirection. + */ + rtp->t_kltp = rtp->t_kltp->kl_realtype; + if (rtp->t_kltp->kl_name && + !strcmp(rtp->t_kltp->kl_name, "char")) { + n->flags |= STRING_FLAG; + } + } + + + /* If this is a pointer to a pointer, get the next + * pointer value. + */ + if (n->type->flag == POINTER_FLAG) { + n->value = *((kaddr_t *)addr); + + /* Set the appropriate node flag values + */ + n->flags |= POINTER_FLAG; + free_node(n->right); + n->left = n->right = (node_t *)NULL; + return(0); + } + /* Zero out the type field in the right child so it doesn't + * accidently get freed up when the right child is freed + * (upon success). + */ + n->right->type = (type_t*)NULL; + free_node(n->right); + n->left = n->right = (node_t *)NULL; + return(0); +} + +/* + * replace_unary() + * + * Convert a unary operator node that contains a pointer to a value + * with a node containing the numerical result. Free the node that + * originally contained the value. + */ +static int +replace_unary(node_t *n, int flags) +{ + uint64_t value; + + if (!n->right) { + set_eval_error(E_MISSING_OPERAND); + error_token = n->tok_ptr; + return(-1); + } + if (is_unary(n->right->operator)) { + if (replace_unary(n->right, flags) == -1) { + return(-1); + } + } + if (n->operator == CAST) { + return(replace_cast(n, flags)); + } else if (n->operator == INDIRECTION) { + return(replace_indirection(n, flags)); + } else if (n->operator == ADDRESS) { + type_t *t; + + if (n->right->node_type == TYPE_DEF) { + if (!(n->right->flags & ADDRESS_FLAG)) { + set_eval_error(E_NO_ADDRESS); + error_token = n->right->tok_ptr; + return(-1); + } + t = n->right->type; + } else { + set_eval_error(E_BAD_TYPE); + error_token = n->right->tok_ptr; + return(-1); + } + n->type = (type_t*)kl_alloc_block(sizeof(type_t)); + n->type->flag = POINTER_FLAG; + n->type->t_next = t; + n->node_type = TYPE_DEF; + n->operator = 0; + n->value = n->right->address; + n->flags = POINTER_FLAG; + if (!(t = eval_type(n))) { + set_eval_error(E_BAD_TYPE); + error_token = n->tok_ptr; + return(-1); + } + n->flags |= t->flag; + n->right->type = 0; + free_nodes(n->right); + n->left = n->right = (node_t *)NULL; + return(0); + } else if (apply_unary(n, &value) == -1) { + return(-1); + } + free_nodes(n->right); + n->node_type = NUMBER; + n->operator = 0; + n->left = n->right = (node_t *)NULL; + memcpy(&n->value, &value, sizeof(uint64_t)); + return(0); +} + +/* + * pointer_to_element() + */ +static void +pointer_to_element(node_t *n0, node_t *n1) +{ + int size; + kltype_t *kltp, *rkltp; + type_t *tp; + + if (!(tp = n0->type)) { + set_eval_error(E_BAD_INDEX); + error_token = n0->tok_ptr; + return; + } + if (tp->t_next->flag == POINTER_FLAG) { + size = sizeof(void *); + } else { + kltp = tp->t_next->t_kltp; + if (!(rkltp = kl_realtype(kltp, 0))) { + set_eval_error(E_BAD_INDEX); + error_token = n0->tok_ptr; + return; + } + size = rkltp->kl_size; + } + + /* Get the details on the array element + */ + n0->flags |= ADDRESS_FLAG; + n0->address = n0->value + (n1->value * size); + n0->type = tp->t_next; + kl_free_block((char *)tp); + if (tp->t_next->flag == POINTER_FLAG) { + n0->flags |= POINTER_FLAG; + n0->value = *((kaddr_t *)n0->address); + } else { + n0->flags &= (~POINTER_FLAG); + n0->value = 0; + } +} + +/* + * array_to_element() + */ +static void +array_to_element(node_t *n0, node_t *n1) +{ + kltype_t *kltp, *rkltp, *ip, *ep; + type_t *tp, *troot = (type_t *)NULL; + + if (!(tp = n0->type)) { + set_eval_error(E_BAD_INDEX); + error_token = n0->tok_ptr; + return; + } + + /* If we are indexing a pointer, then make a call to the + * pointer_to_element() and return. + */ + if (tp->flag == POINTER_FLAG) { + return(pointer_to_element(n0, n1)); + } + + if (!(kltp = n0->type->t_kltp)) { + set_eval_error(E_BAD_INDEX); + error_token = n0->tok_ptr; + return; + } + if (!(rkltp = kl_realtype(kltp, KLT_ARRAY))) { + set_eval_error(E_BAD_INDEX); + error_token = n0->tok_ptr; + return; + } + ip = rkltp->kl_indextype; + ep = rkltp->kl_elementtype; + if (!ip || !ep) { + set_eval_error(E_BAD_INDEX); + error_token = n1->tok_ptr; + return; + } + /* Get the details on the array element + */ + n0->address = n0->address + (n1->value * ep->kl_size); + if (ep->kl_type == KLT_POINTER) { + n0->flags |= POINTER_FLAG; + n0->value = *((kaddr_t *)n0->address); + } else { + n0->value = 0; + } + n0->flags |= ADDRESS_FLAG; + kltp = ep; + while (kltp->kl_type == KLT_POINTER) { + if (troot) { + tp->t_next = (type_t*)kl_alloc_block(sizeof(type_t)); + tp = tp->t_next; + } else { + tp = (type_t*)kl_alloc_block(sizeof(type_t)); + troot = tp; + } + tp->flag = POINTER_FLAG; + kltp = kltp->kl_realtype; + } + if (troot) { + tp->t_next = (type_t*)kl_alloc_block(sizeof(type_t)); + tp = tp->t_next; + n0->type = troot; + } else { + tp = (type_t*)kl_alloc_block(sizeof(type_t)); + n0->type = tp; + } + tp->flag = KLTYPE_FLAG; + tp->t_kltp = ep; +} + +/* + * number_to_size() + */ +int +number_to_size(node_t *np) +{ + int unsigned_flag = 0; + + if (np->node_type != NUMBER) { + set_eval_error(E_BAD_TYPE); + error_token = np->tok_ptr; + return(0); + } + if (np->flags & UNSIGNED_FLAG) { + unsigned_flag = 1; + } + if ((np->value >= 0) && (np->value <= 0xffffffff)) { + return(4); + } else if (((np->value >> 32) & 0xffffffff) == 0xffffffff) { + if (unsigned_flag) { + return(8); + } else if (sizeof(void *) == 4) { + return(4); + } else { + return(8); + } + } + return(8); +} + +/* + * number_to_type() + */ +kltype_t * +number_to_type(node_t *np) +{ + int unsigned_flag = 0; + kltype_t *kltp, *rkltp = (kltype_t *)NULL; + + if (np->node_type != NUMBER) { + set_eval_error(E_BAD_TYPE); + error_token = np->tok_ptr; + return((kltype_t *)NULL); + } + if (np->flags & UNSIGNED_FLAG) { + unsigned_flag = 1; + } + if ((np->value >= 0) && (np->value <= 0xffffffff)) { + if (unsigned_flag) { + kltp = kl_find_type("uint32_t", KLT_TYPEDEF); + } else { + kltp = kl_find_type("int32_t", KLT_TYPEDEF); + } + } else if (((np->value >> 32) & 0xffffffff) == 0xffffffff) { + if (unsigned_flag) { + kltp = kl_find_type("uint64_t", KLT_TYPEDEF); + } else if (sizeof(void *) == 4) { + kltp = kl_find_type("int32_t", KLT_TYPEDEF); + } else { + kltp = kl_find_type("int64_t", KLT_TYPEDEF); + } + } else { + if (unsigned_flag) { + kltp = kl_find_type("uint64_t", KLT_TYPEDEF); + } else { + kltp = kl_find_type("int64_t", KLT_TYPEDEF); + } + } + if (kltp) { + if (!(rkltp = kl_realtype(kltp, 0))) { + rkltp = kltp; + } + } else { + set_eval_error(E_BAD_TYPE); + error_token = np->tok_ptr; + } + return(rkltp); +} + +/* + * type_to_number() + * + * Convert a base type to a numeric value. Return 1 on successful + * conversion, 0 if nothing was done. + */ +static int +type_to_number(node_t *np) +{ + int byte_size, bit_offset, bit_size, encoding; + uint64_t value, value1; + kltype_t *kltp, *rkltp; + + /* Sanity check... + */ + if (np->node_type != TYPE_DEF) { + set_eval_error(E_NOTYPE); + error_token = np->tok_ptr; + return(0); + } + if (!np->type) { + set_eval_error(E_NOTYPE); + error_token = np->tok_ptr; + return(0); + } + if (np->type->flag == POINTER_FLAG) { + return(0); + } + + /* Get the real type record and make sure that it is + * for a base type. + */ + kltp = np->type->t_kltp; + rkltp = kl_realtype(kltp, 0); + if (rkltp->kl_type != KLT_BASE) { + set_eval_error(E_NOTYPE); + error_token = np->tok_ptr; + return(0); + } + + byte_size = rkltp->kl_size; + bit_offset = rkltp->kl_bit_offset; + if (!(bit_size = rkltp->kl_bit_size)) { + bit_size = byte_size * 8; + } + encoding = rkltp->kl_encoding; + if (np->flags & ADDRESS_FLAG) { + /* FIXME: untested */ + if (invalid_address(np->address, byte_size)) { + kdb_printf("ILLEGAL ADDRESS (%lx)", + (uaddr_t)np->address); + return (0); + } + kl_get_block(np->address, byte_size,(void *)&value1,(void *)0); + } else { + value1 = np->value; + } + value = kl_get_bit_value(&value1, byte_size, bit_size, bit_offset); + switch (byte_size) { + + case 1 : + if (encoding == ENC_UNSIGNED) { + np->value = (unsigned char)value; + np->flags |= UNSIGNED_FLAG; + } else if (encoding == ENC_SIGNED) { + np->value = (signed char)value; + } else { + np->value = (char)value; + } + break; + + case 2 : + if (encoding == ENC_UNSIGNED) { + np->value = (uint16_t)value; + np->flags |= UNSIGNED_FLAG; + } else { + np->value = (int16_t)value; + } + break; + + case 4 : + if (encoding == ENC_UNSIGNED) { + np->value = (uint32_t)value; + np->flags |= UNSIGNED_FLAG; + } else { + np->value = (int32_t)value; + } + break; + + case 8 : + if (encoding == ENC_UNSIGNED) { + np->value = (uint64_t)value; + np->flags |= UNSIGNED_FLAG; + } else { + np->value = (int64_t)value; + } + break; + + default : + set_eval_error(E_BAD_TYPE); + error_token = np->tok_ptr; + return(0); + } + np->byte_size = byte_size; + np->node_type = NUMBER; + return(1); +} + +/* + * eval_type() + */ +static type_t * +eval_type(node_t *n) +{ + type_t *t; + + if (!(t = n->type)) { + return((type_t*)NULL); + } + while (t->flag == POINTER_FLAG) { + t = t->t_next; + + /* If for some reason, there is no type pointer (this shouldn't + * happen but...), we have to make sure that we don't try to + * reference a NULL pointer and get a SEGV. Return an error if + * 't' is NULL. + */ + if (!t) { + return((type_t*)NULL); + } + } + if (t->flag == KLTYPE_FLAG) { + return (t); + } + return((type_t*)NULL); +} + +/* + * expand_variables() + */ +static char * +expand_variables(char *exp, int flags) +{ + return((char *)NULL); +} + +/* + * eval() + */ +node_t * +eval(char **exp, int flags) +{ + token_t *tok; + node_t *n, *root; + char *e, *s; + + eval_error = 0; + logical_flag = 0; + + /* Make sure there is an expression to evaluate + */ + if (!(*exp)) { + return ((node_t*)NULL); + } + + /* Expand any variables that are in the expression string. If + * a new string is allocated by the expand_variables() function, + * we need to make sure the original expression string gets + * freed. In any event, point s at the current expression string + * so that it gets freed up when we are done. + */ + if ((e = expand_variables(*exp, 0))) { + kl_free_block((void *)*exp); + *exp = e; + } else if (eval_error) { + eval_error |= E_BAD_EVAR; + error_token = *exp; + } + s = *exp; + tok = get_token_list(s); + if (eval_error) { + return((node_t*)NULL); + } + + /* Get the node_list and evaluate the expression. + */ + node_list = get_node_list(tok, flags); + if (eval_error) { + free_nodelist(node_list); + node_list = (node_t*)NULL; + free_tokens(tok); + return((node_t*)NULL); + } + if (!(n = do_eval(flags))) { + if (!eval_error) { + set_eval_error(E_SYNTAX_ERROR); + error_token = s + strlen(s) - 1; + } + free_nodes(n); + free_tokens(tok); + return((node_t*)NULL); + } + + if (!(root = replace(n, flags))) { + if (eval_error) { + free_nodes(n); + free_tokens(tok); + return((node_t*)NULL); + } + root = n; + } + + /* Check to see if the the result should + * be interpreted as 'true' or 'false' + */ + if (logical_flag && ((root->value == 0) || (root->value == 1))) { + root->flags |= BOOLIAN_FLAG; + } + free_tokens(tok); + return(root); +} + +/* + * print_number() + */ +void +print_number(node_t *np, int flags) +{ + int size; + unsigned long long value; + + if ((size = number_to_size(np)) && (size != sizeof(uint64_t))) { + value = np->value & (((uint64_t)1 << (uint64_t)(size*8))-1); + } else { + value = np->value; + } + if (flags & C_HEX) { + kdb_printf("0x%llx", value); + } else if (flags & C_BINARY) { + kdb_printf("0b"); + kl_binary_print(value); + } else { + if (np->flags & UNSIGNED_FLAG) { + kdb_printf("%llu", value); + } else { + kdb_printf("%lld", np->value); + } + } +} + +/* + * print_string() + */ +void +print_string(kaddr_t addr, int size) +{ + int i; + char *str; + + if (!size) { + size = 255; + } + /* FIXME: untested */ + if (invalid_address(addr, size)) { + klib_error = KLE_INVALID_PADDR; + return; + } + str = (char*)kl_alloc_block(size); + kl_get_block(addr, size, (void *)str, (void *)0); + kdb_printf("\"%s", str); + for (i = 0; i < size; i++) { + if (!str[i]) { + break; + } + } + if (KL_ERROR || (i == size)) { + kdb_printf("..."); + } + kdb_printf("\""); + kl_free_block(str); +} + +/* + * kl_print_error() + */ +void +kl_print_error(void) +{ + int ecode; + + ecode = klib_error & 0xffffffff; + switch(ecode) { + + /** General klib error codes + **/ + case KLE_NO_MEMORY: + kdb_printf("insufficient memory"); + break; + case KLE_OPEN_ERROR: + kdb_printf("unable to open file"); + break; + case KLE_ZERO_BLOCK: + kdb_printf("tried to allocate a zero-sized block"); + break; + case KLE_INVALID_VALUE: + kdb_printf("invalid input value"); + break; + case KLE_NULL_BUFF: + kdb_printf( "NULL buffer pointer"); + break; + case KLE_ZERO_SIZE: + kdb_printf("zero sized block requested"); + break; + case KLE_ACTIVE: + kdb_printf("operation not supported on a live system"); + break; + case KLE_UNSUPPORTED_ARCH: + kdb_printf("unsupported architecture"); + break; + case KLE_MISC_ERROR: + kdb_printf("KLIB error"); + break; + case KLE_NOT_SUPPORTED: + kdb_printf("operation not supported"); + break; + case KLE_UNKNOWN_ERROR: + kdb_printf("unknown error"); + break; + + /** memory error codes + **/ + case KLE_BAD_MAP_FILE: + kdb_printf("bad map file"); + break; + case KLE_BAD_DUMP: + kdb_printf("bad dump file"); + break; + case KLE_BAD_DUMPTYPE: + kdb_printf("bad dumptype"); + break; + case KLE_INVALID_LSEEK: + kdb_printf("lseek error"); + break; + case KLE_INVALID_READ: + kdb_printf("not found in dump file"); + break; + case KLE_BAD_KERNINFO: + kdb_printf("bad kerninfo struct"); + break; + case KLE_INVALID_PADDR: + kdb_printf("invalid physical address"); + break; + case KLE_INVALID_VADDR: + kdb_printf("invalid virtual address"); + break; + case KLE_INVALID_VADDR_ALIGN: + kdb_printf("invalid vaddr alignment"); + break; + case KLE_INVALID_MAPPING: + kdb_printf("invalid address mapping"); + break; + case KLE_PAGE_NOT_PRESENT: + kdb_printf("page not present"); + break; + case KLE_BAD_ELF_FILE: + kdb_printf("bad elf file"); + break; + case KLE_ARCHIVE_FILE: + kdb_printf("archive file"); + break; + case KLE_MAP_FILE_PRESENT: + kdb_printf("map file present"); + break; + case KLE_BAD_MAP_FILENAME: + kdb_printf("bad map filename"); + break; + case KLE_BAD_DUMP_FILENAME: + kdb_printf("bad dump filename"); + break; + case KLE_BAD_NAMELIST_FILE: + kdb_printf("bad namelist file"); + break; + case KLE_BAD_NAMELIST_FILENAME: + kdb_printf("bad namelist filename"); + break; + + /** symbol error codes + **/ + case KLE_NO_SYMTAB: + kdb_printf("no symtab"); + break; + case KLE_NO_SYMBOLS: + kdb_printf("no symbol information"); + break; + case KLE_NO_MODULE_LIST: + kdb_printf("kernel without module support"); + break; + + /** kernel data error codes + **/ + case KLE_INVALID_KERNELSTACK: + kdb_printf("invalid kernel stack"); + break; + case KLE_INVALID_STRUCT_SIZE: + kdb_printf("invalid struct size"); + break; + case KLE_BEFORE_RAM_OFFSET: + kdb_printf("physical address proceeds start of RAM"); + break; + case KLE_AFTER_MAXPFN: + kdb_printf("PFN exceeds maximum PFN"); + break; + case KLE_AFTER_PHYSMEM: + kdb_printf("address exceeds physical memory"); + break; + case KLE_AFTER_MAXMEM: + kdb_printf("address exceeds maximum physical address"); + break; + case KLE_PHYSMEM_NOT_INSTALLED: + kdb_printf("physical memory not installed"); + break; + case KLE_NO_DEFTASK: + kdb_printf("default task not set"); + break; + case KLE_PID_NOT_FOUND: + kdb_printf("PID not found"); + break; + case KLE_DEFTASK_NOT_ON_CPU: + kdb_printf("default task not running on a cpu"); + break; + case KLE_NO_CURCPU: + kdb_printf("current cpu could not be determined"); + break; + + case KLE_KERNEL_MAGIC_MISMATCH: + kdb_printf("kernel_magic mismatch " + "of map and memory image"); + break; + + case KLE_INVALID_DUMP_HEADER: + kdb_printf("invalid dump header in dump"); + break; + + case KLE_DUMP_INDEX_CREATION: + kdb_printf("cannot create index file"); + break; + + case KLE_DUMP_HEADER_ONLY: + kdb_printf("dump only has a dump header"); + break; + + case KLE_NO_END_SYMBOL: + kdb_printf("no _end symbol in kernel"); + break; + + case KLE_NO_CPU: + kdb_printf("CPU not installed"); + break; + + default: + break; + } + kdb_printf("\n"); +} + +/* + * kl_print_string() + * + * print out a string, translating all embeded control characters + * (e.g., '\n' for newline, '\t' for tab, etc.) + */ +void +kl_print_string(char *s) +{ + char *sp, *cp; + + kl_reset_error(); + + if (!(sp = s)) { + klib_error = KLE_BAD_STRING; + return; + } + /* FIXME: untested */ + if (invalid_address((kaddr_t)sp, 1)) { + klib_error = KLE_INVALID_PADDR; + return; + } + + while (sp) { + if ((cp = strchr(sp, '\\'))) { + switch (*(cp + 1)) { + + case 'n' : + *cp++ = '\n'; + *cp++ = 0; + break; + + case 't' : + *cp++ = '\t'; + *cp++ = 0; + break; + + default : + if (*(cp + 1) == 0) { + klib_error = KLE_BAD_STRING; + return; + } + /* Change the '\' character to a zero + * and then print the string (the rest + * of the string will be picked + * up on the next pass). + */ + *cp++ = 0; + break; + } + kdb_printf("%s", sp); + sp = cp; + } else { + kdb_printf("%s", sp); + sp = 0; + } + } +} + +/* + * print_eval_results() + */ +int +print_eval_results(node_t *np, int flags) +{ + int size, i, count, ptr_cnt = 0; + kaddr_t addr; + char *typestr; + kltype_t *kltp, *rkltp = NULL, *nkltp; + type_t *tp; + + /* Print the results + */ + switch (np->node_type) { + + case NUMBER: + print_number(np, flags); + break; + + case TYPE_DEF: { + + /* First, determine the number of levels of indirection + * by determining the number of pointer type records. + */ + if ((tp = np->type)) { + while (tp && (tp->flag == POINTER_FLAG)) { + ptr_cnt++; + tp = tp->t_next; + } + if (tp) { + rkltp = tp->t_kltp; + } + } + if (!rkltp) { + kdb_printf("Type information not available\n"); + return(1); + } + + if (ptr_cnt) { + + /* If this is a member, we need to get the + * first type record. + */ + if (rkltp->kl_type == KLT_MEMBER) { + /* We need to get down to the first + * real type record... + */ + rkltp = rkltp->kl_realtype; + } + + /* step over any KLT_POINTER type records. + */ + while (rkltp && rkltp->kl_type == KLT_POINTER) { + rkltp = rkltp->kl_realtype; + } + if (!rkltp) { + kdb_printf("Bad type information\n"); + return(1); + } + typestr = rkltp->kl_typestr; + if (rkltp->kl_type == KLT_FUNCTION) { + kdb_printf("%s(", typestr); + } else if (rkltp->kl_type == KLT_ARRAY) { + kdb_printf("(%s(", typestr); + } else { + kdb_printf("(%s", typestr); + } + for (i = 0; i < ptr_cnt; i++) { + kdb_printf("*"); + } + if (rkltp->kl_type == KLT_FUNCTION) { + kdb_printf(")("); + } else if (rkltp->kl_type == KLT_ARRAY) { + kdb_printf(")"); + + nkltp = rkltp; + while (nkltp->kl_type == KLT_ARRAY) { + count = nkltp->kl_high_bounds - + nkltp->kl_low_bounds + 1; + kdb_printf("[%d]", count); + nkltp = nkltp->kl_elementtype; + } + } + kdb_printf(") "); + kdb_printf("0x%llx", np->value); + + if (ptr_cnt > 1) { + break; + } + + if ((rkltp->kl_type == KLT_BASE) && + rkltp->kl_encoding == ENC_CHAR) { + kdb_printf(" = "); + print_string(np->value, 0); + } + break; + } + if (np->flags & KLTYPE_FLAG) { + void * ptr; + + /* Get the type information. It's possible + * that the type is a member. In which case, + * the size may only be from this record + * (which would be the casse if this is an + * array). We must check the original type + * record first, and try the realtype record + * if the value is zero. + */ + kltp = np->type->t_kltp; + + if (kltp->kl_type == KLT_MEMBER) { + rkltp = kltp->kl_realtype; + } else { + rkltp = kltp; + } + + /* Check to see if this is a typedef. If + * it is, then it might be a typedef for + * a pointer type. Don't walk to the last + * type record. + */ + while (rkltp->kl_type == KLT_TYPEDEF) { + rkltp = rkltp->kl_realtype; + } + + if (rkltp->kl_type == KLT_POINTER) { + kdb_printf("0x%llx", np->value); + break; + } + if((rkltp->kl_name != 0) && + !(strcmp(rkltp->kl_name, "void"))) { + /* we are about to dereference + * a void pointer. + */ + kdb_printf("Can't dereference a " + "generic pointer.\n"); + return(1); + } + + size = rkltp->kl_size; + if (!size || (size < 0)) { + size = kltp->kl_size; + } + + if(rkltp->kl_type==KLT_ARRAY) { + size = rkltp->kl_high_bounds - + rkltp->kl_low_bounds + 1; + if(rkltp->kl_elementtype == NULL){ + kdb_printf("Incomplete array" + " type.\n"); + return(1); + } + if(rkltp->kl_elementtype->kl_type == + KLT_POINTER){ + size *= sizeof(void *); + } else { + size *= rkltp->kl_elementtype->kl_size; + } + } + if(size){ + ptr = kl_alloc_block(size); + } else { + ptr = NULL; + } + if ((rkltp->kl_type == KLT_BASE) && + !(np->flags & ADDRESS_FLAG)) { + switch (size) { + case 1: + *(unsigned char *)ptr = + np->value; + break; + + case 2: + *(unsigned short *)ptr = + np->value; + break; + + case 4: + *(unsigned int *)ptr = + np->value; + break; + + case 8: + *(unsigned long long *) + ptr = np->value; + break; + } + kl_print_type(ptr, rkltp, 0, + flags|SUPPRESS_NAME); + kl_free_block(ptr); + return(1); + } + + if(size){ + addr = np->address; + if (invalid_address(addr, size)) { + kdb_printf ( + "invalid address %#lx\n", + addr); + return 1; + } + kl_get_block(addr, size, (void *)ptr, + (void *)0); + if (KL_ERROR) { + kl_print_error(); + kl_free_block(ptr); + return(1); + } + } + /* Print out the actual type + */ + switch (rkltp->kl_type) { + case KLT_STRUCT: + case KLT_UNION: + kl_print_type(ptr, rkltp, 0, + flags); + break; + + case KLT_ARRAY: + kl_print_type(ptr, rkltp, 0, + flags| SUPPRESS_NAME); + break; + + default: + kl_print_type(ptr, rkltp, 0, + (flags| + SUPPRESS_NAME| + SUPPRESS_NL)); + break; + } + if(ptr){ + kl_free_block(ptr); + } + } + break; + } + + case VADDR: + /* If we get here, there was no type info available. + * The ADDRESS_FLAG should be set (otherwise we + * would have returned an error). So, print out + * the address. + */ + kdb_printf("0x%lx", np->address); + break; + + default: + if (np->node_type == TEXT) { + kl_print_string(np->name); + if (KL_ERROR) { + kl_print_error(); + return(1); + } + } else if (np->node_type == CHARACTER) { + kdb_printf("\'%c\'", (char)np->value); + } + break; + } + return(0); +} + +/* + * print_eval_error() + */ +void +print_eval_error( + char *cmdname, + char *s, + char *bad_ptr, + uint64_t error, + int flags) +{ + int i, cmd_len; + + kdb_printf("%s %s\n", cmdname, s); + cmd_len = strlen(cmdname); + + if (!bad_ptr) { + for (i = 0; i < (strlen(s) + cmd_len); i++) { + kdb_printf(" "); + } + } else { + for (i = 0; i < (bad_ptr - s + 1 + cmd_len); i++) { + kdb_printf(" "); + } + } + kdb_printf("^ "); + switch (error) { + case E_OPEN_PAREN : + kdb_printf("Too many open parenthesis\n"); + break; + + case E_CLOSE_PAREN : + kdb_printf("Too many close parenthesis\n"); + break; + + case E_BAD_STRUCTURE : + kdb_printf("Invalid structure\n"); + break; + + case E_MISSING_STRUCTURE : + kdb_printf("Missing structure\n"); + break; + + case E_BAD_MEMBER : + kdb_printf("No such member\n"); + break; + + case E_BAD_OPERATOR : + kdb_printf("Invalid operator\n"); + break; + + case E_MISSING_OPERAND : + kdb_printf("Missing operand\n"); + break; + + case E_BAD_OPERAND : + kdb_printf("Invalid operand\n"); + break; + + case E_BAD_TYPE : + kdb_printf("Invalid type\n"); + if (!have_debug_file) { + kdb_printf("no debuginfo file\n"); + return; + } + break; + + case E_NOTYPE : + kdb_printf("Could not find type information\n"); + break; + + case E_BAD_POINTER : + kdb_printf("Invalid pointer\n"); + break; + + case E_BAD_INDEX : + kdb_printf("Invalid array index\n"); + break; + + case E_BAD_CHAR : + kdb_printf("Invalid character value\n"); + break; + + case E_BAD_STRING : + kdb_printf("Non-termining string\n"); + break; + + case E_END_EXPECTED : + kdb_printf( + "Expected end of print statement\n"); + break; + + case E_BAD_EVAR : + kdb_printf("Invalid eval variable\n"); + break; + + case E_BAD_VALUE : + kdb_printf("Invalid value\n"); + break; + + case E_NO_VALUE : + kdb_printf("No value supplied\n"); + break; + + case E_DIVIDE_BY_ZERO : + kdb_printf("Divide by zero\n"); + break; + + case E_BAD_CAST : + kdb_printf("Invalid cast\n"); + break; + + case E_NO_ADDRESS : + kdb_printf("Not an address\n"); + break; + + case E_SINGLE_QUOTE : + kdb_printf("Missing single quote\n"); + break; + + case E_BAD_WHATIS : + kdb_printf("Invalid whatis Operation\n"); + break; + + case E_NOT_IMPLEMENTED : + kdb_printf("Not implemented\n"); + break; + + default : + kdb_printf("Syntax error\n"); + break; + } +} + +/* + * single_type() + */ +void +single_type(char *str) +{ + char buffer[256], *type_name; + kltype_t *kltp; + syment_t *sp; + + type_name = buffer; + strcpy(type_name, str); + + if (have_debug_file) { + if ((kltp = kl_find_type(type_name, KLT_TYPE))) { + kl_print_type((void *)NULL, kltp, 0, C_SHOWOFFSET); + return; + } + if ((kltp = kl_find_type(type_name, KLT_TYPEDEF))) { + kdb_printf ("typedef %s:\n", type_name); + kl_print_type((void *)NULL, kltp, 0, C_SHOWOFFSET); + return; + } + } + if ((sp = kl_lkup_symname(type_name))) { + kdb_printf ("symbol %s value: %#lx\n", str, sp->s_addr); + kl_free_block((void *)sp); + return; + } + kdb_printf("could not find type or symbol information for %s\n", + type_name); + return; +} + +/* + * sizeof_type() + */ +void +sizeof_type(char *str) +{ + char buffer[256], *type_name; + kltype_t *kltp; + + type_name = buffer; + strcpy(type_name, str); + + if ((kltp = kl_find_type(type_name, KLT_TYPE))) { + kdb_printf ("%s %d %#x\n", kltp->kl_typestr, + kltp->kl_size, kltp->kl_size); + return; + } + if ((kltp = kl_find_type(type_name, KLT_TYPEDEF))) { + kdb_printf ("%s %d %#x\n", kltp->kl_typestr, + kltp->kl_size, kltp->kl_size); + return; + } + kdb_printf("could not find type information for %s\n", type_name); +} + +EXPORT_SYMBOL(have_debug_file); +EXPORT_SYMBOL(type_tree); +EXPORT_SYMBOL(typedef_tree); + +#if defined(CONFIG_X86_32) +/* needed for i386: */ +#include +#include +/* + * Generic C version of full 64 bit by 64 bit division + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * Code generated for this function might be very inefficient + * for some CPUs, can be overridden by linking arch-specific + * assembly versions such as arch/sparc/lib/udivdi.S + */ +uint64_t +__udivdi3(uint64_t dividend, uint64_t divisor) +{ + uint32_t d = divisor; + /* Scale divisor to 32 bits */ + if (divisor > 0xffffffffULL) { + unsigned int shift = fls(divisor >> 32); + d = divisor >> shift; + dividend >>= shift; + } + /* avoid 64 bit division if possible */ + if (dividend >> 32) + do_div(dividend, d); + else + dividend = (uint32_t) dividend / d; + return dividend; +} + +int64_t +__divdi3(int64_t dividend, int64_t divisor) +{ + int32_t d = divisor; + /* Scale divisor to 32 bits */ + if (divisor > 0xffffffffLL) { + unsigned int shift = fls(divisor >> 32); + d = divisor >> shift; + dividend >>= shift; + } + /* avoid 64 bit division if possible */ + if (dividend >> 32) + do_div(dividend, d); + else + dividend = (int32_t) dividend / d; + return dividend; +} + +uint64_t +__umoddi3(uint64_t dividend, uint64_t divisor) +{ + return dividend - (__udivdi3(dividend, divisor) * divisor); +} + +int64_t +__moddi3(int64_t dividend, int64_t divisor) +{ + return dividend - (__divdi3(dividend, divisor) * divisor); +} +#endif /* CONFIG_x86_32 */ --- /dev/null +++ b/kdb/kdbmain.c @@ -0,0 +1,4333 @@ +/* + * Kernel Debugger Architecture Independent Main Code + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (C) 2000 Stephane Eranian + * Xscale (R) modifications copyright (C) 2003 Intel Corporation. + */ + +/* + * Updated for Xscale (R) architecture support + * Eddie Dong 8 Jan 03 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_LKCD_DUMP) || defined(CONFIG_LKCD_DUMP_MODULE) +#include +#endif +#include +#include +#ifdef CONFIG_KDB_KDUMP +#include +#endif + +#include + +#include +#include +#include +#include +#include +char kdb_debug_info_filename[256] = {""}; +EXPORT_SYMBOL(kdb_debug_info_filename); +#define GREP_LEN 256 +char kdb_grep_string[GREP_LEN]; +int kdb_grepping_flag; +EXPORT_SYMBOL(kdb_grepping_flag); +int kdb_grep_leading; +int kdb_grep_trailing; + +/* + * Kernel debugger state flags + */ +volatile int kdb_flags; +atomic_t kdb_event; +atomic_t kdb_8250; + +/* + * kdb_lock protects updates to kdb_initial_cpu. Used to + * single thread processors through the kernel debugger. + */ +static DEFINE_SPINLOCK(kdb_lock); +volatile int kdb_initial_cpu = -1; /* cpu number that owns kdb */ +int kdb_seqno = 2; /* how many times kdb has been entered */ + +volatile int kdb_nextline = 1; +static volatile int kdb_new_cpu; /* Which cpu to switch to */ + +volatile int kdb_state[NR_CPUS]; /* Per cpu state */ + +const struct task_struct *kdb_current_task; +EXPORT_SYMBOL(kdb_current_task); +struct pt_regs *kdb_current_regs; + +#ifdef CONFIG_KDB_OFF +int kdb_on = 0; /* Default is off */ +#else +int kdb_on = 1; /* Default is on */ +#endif /* CONFIG_KDB_OFF */ + +const char *kdb_diemsg; +static int kdb_go_count; +#ifdef CONFIG_KDB_CONTINUE_CATASTROPHIC +static unsigned int kdb_continue_catastrophic = CONFIG_KDB_CONTINUE_CATASTROPHIC; +#else +static unsigned int kdb_continue_catastrophic = 0; +#endif + +#ifdef kdba_setjmp + /* + * Must have a setjmp buffer per CPU. Switching cpus will + * cause the jump buffer to be setup for the new cpu, and + * subsequent switches (and pager aborts) will use the + * appropriate per-processor values. + */ +kdb_jmp_buf *kdbjmpbuf; +#endif /* kdba_setjmp */ + + /* + * kdb_commands describes the available commands. + */ +static kdbtab_t *kdb_commands; +static int kdb_max_commands; + +typedef struct _kdbmsg { + int km_diag; /* kdb diagnostic */ + char *km_msg; /* Corresponding message text */ +} kdbmsg_t; + +#define KDBMSG(msgnum, text) \ + { KDB_##msgnum, text } + +static kdbmsg_t kdbmsgs[] = { + KDBMSG(NOTFOUND,"Command Not Found"), + KDBMSG(ARGCOUNT, "Improper argument count, see usage."), + KDBMSG(BADWIDTH, "Illegal value for BYTESPERWORD use 1, 2, 4 or 8, 8 is only allowed on 64 bit systems"), + KDBMSG(BADRADIX, "Illegal value for RADIX use 8, 10 or 16"), + KDBMSG(NOTENV, "Cannot find environment variable"), + KDBMSG(NOENVVALUE, "Environment variable should have value"), + KDBMSG(NOTIMP, "Command not implemented"), + KDBMSG(ENVFULL, "Environment full"), + KDBMSG(ENVBUFFULL, "Environment buffer full"), + KDBMSG(TOOMANYBPT, "Too many breakpoints defined"), +#ifdef CONFIG_CPU_XSCALE + KDBMSG(TOOMANYDBREGS, "More breakpoints than ibcr registers defined"), +#else + KDBMSG(TOOMANYDBREGS, "More breakpoints than db registers defined"), +#endif + KDBMSG(DUPBPT, "Duplicate breakpoint address"), + KDBMSG(BPTNOTFOUND, "Breakpoint not found"), + KDBMSG(BADMODE, "Invalid IDMODE"), + KDBMSG(BADINT, "Illegal numeric value"), + KDBMSG(INVADDRFMT, "Invalid symbolic address format"), + KDBMSG(BADREG, "Invalid register name"), + KDBMSG(BADCPUNUM, "Invalid cpu number"), + KDBMSG(BADLENGTH, "Invalid length field"), + KDBMSG(NOBP, "No Breakpoint exists"), + KDBMSG(BADADDR, "Invalid address"), +}; +#undef KDBMSG + +static const int __nkdb_err = sizeof(kdbmsgs) / sizeof(kdbmsg_t); + + +/* + * Initial environment. This is all kept static and local to + * this file. We don't want to rely on the memory allocation + * mechanisms in the kernel, so we use a very limited allocate-only + * heap for new and altered environment variables. The entire + * environment is limited to a fixed number of entries (add more + * to __env[] if required) and a fixed amount of heap (add more to + * KDB_ENVBUFSIZE if required). + */ + +static char *__env[] = { +#if defined(CONFIG_SMP) + "PROMPT=[%d]kdb> ", + "MOREPROMPT=[%d]more> ", +#else + "PROMPT=kdb> ", + "MOREPROMPT=more> ", +#endif + "RADIX=16", + "LINES=24", + "COLUMNS=80", + "MDCOUNT=8", /* lines of md output */ + "BTARGS=9", /* 9 possible args in bt */ + KDB_PLATFORM_ENV, + "DTABCOUNT=30", + "NOSECT=1", + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, + (char *)0, +}; + +static const int __nenv = (sizeof(__env) / sizeof(char *)); + +/* external commands: */ +int kdb_debuginfo_print(int argc, const char **argv); +int kdb_pxhelp(int argc, const char **argv); +int kdb_walkhelp(int argc, const char **argv); +int kdb_walk(int argc, const char **argv); + +/* + * kdb_serial_str is the sequence that the user must enter on a serial + * console to invoke kdb. It can be a single character such as "\001" + * (control-A) or multiple characters such as "\eKDB". NOTE: All except the + * last character are passed through to the application reading from the serial + * console. + * + * I tried to make the sequence a CONFIG_ option but most of CML1 cannot cope + * with '\' in strings. CML2 would have been able to do it but we lost CML2. + * KAO. + */ +const char kdb_serial_str[] = "\eKDB"; +EXPORT_SYMBOL(kdb_serial_str); + +struct task_struct * +kdb_curr_task(int cpu) +{ + struct task_struct *p = curr_task(cpu); +#ifdef _TIF_MCA_INIT + struct kdb_running_process *krp = kdb_running_process + cpu; + if ((task_thread_info(p)->flags & _TIF_MCA_INIT) && krp->p) + p = krp->p; +#endif + return p; +} + +/* + * 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 NULL; +} + +/* + * kdballocenv + * + * This function is used to allocate bytes for environment entries. + * + * Parameters: + * match A character string representing a numeric value + * Outputs: + * *value the unsigned long represntation of the env variable 'match' + * Returns: + * Zero on success, a kdb diagnostic on failure. + * Locking: + * No locking considerations required. Must be called with all + * processors halted. + * Remarks: + * We use a static environment buffer (envbuffer) to hold the values + * of dynamically generated environment variables (see kdb_set). Buffer + * space once allocated is never free'd, so over time, the amount of space + * (currently 512 bytes) will be exhausted if env variables are changed + * frequently. + */ +static char * +kdballocenv(size_t bytes) +{ +#define KDB_ENVBUFSIZE 512 + static char envbuffer[KDB_ENVBUFSIZE]; + static int envbufsize; + char *ep = NULL; + + 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: + */ + +static 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, NULL, 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; +} + +/* + * 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 + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +static int +kdb_set(int argc, const char **argv) +{ + int i; + char *ep; + size_t varlen, vallen; + + /* + * we can be invoked two ways: + * set var=value argv[1]="var", argv[2]="value" + * set var = value argv[1]="var", argv[2]="=", argv[3]="value" + * - if the latter, shift 'em down. + */ + if (argc == 3) { + argv[2] = argv[3]; + argc--; + } + + if (argc != 2) + return KDB_ARGCOUNT; + + /* + * Check for internal variables + */ + if (strcmp(argv[1], "KDBDEBUG") == 0) { + unsigned int debugflags; + char *cp; + + debugflags = simple_strtoul(argv[2], &cp, 0); + if (cp == argv[2] || debugflags & ~KDB_DEBUG_FLAG_MASK) { + kdb_printf("kdb: illegal debug flags '%s'\n", + argv[2]); + return 0; + } + kdb_flags = (kdb_flags & ~(KDB_DEBUG_FLAG_MASK << KDB_DEBUG_FLAG_SHIFT)) + | (debugflags << KDB_DEBUG_FLAG_SHIFT); + + return 0; + } + + /* + * Tokenizer squashed the '=' sign. argv[1] is variable + * name, argv[2] = value. + */ + varlen = strlen(argv[1]); + vallen = strlen(argv[2]); + ep = kdballocenv(varlen + vallen + 2); + if (ep == (char *)0) + return KDB_ENVBUFFULL; + + sprintf(ep, "%s=%s", argv[1], argv[2]); + + ep[varlen+vallen+1]='\0'; + + for(i=0; i<__nenv; i++) { + if (__env[i] + && ((strncmp(__env[i], argv[1], varlen)==0) + && ((__env[i][varlen] == '\0') + || (__env[i][varlen] == '=')))) { + __env[i] = ep; + return 0; + } + } + + /* + * Wasn't existing variable. Fit into slot. + */ + for(i=0; i<__nenv-1; i++) { + if (__env[i] == (char *)0) { + __env[i] = ep; + return 0; + } + } + + return KDB_ENVFULL; +} + +static int +kdb_check_regs(void) +{ + if (!kdb_current_regs) { + kdb_printf("No current kdb registers." + " You may need to select another task\n"); + return KDB_BADREG; + } + return 0; +} + +/* + * kdbgetaddrarg + * + * This function is responsible for parsing an + * address-expression and returning the value of + * the expression, symbol name, and offset to the caller. + * + * The argument may consist of a numeric value (decimal or + * hexidecimal), a symbol name, a register name (preceeded + * by the percent sign), an environment variable with a numeric + * value (preceeded by a dollar sign) or a simple arithmetic + * expression consisting of a symbol name, +/-, and a numeric + * constant value (offset). + * + * Parameters: + * argc - count of arguments in argv + * argv - argument vector + * *nextarg - index to next unparsed argument in argv[] + * regs - Register state at time of KDB entry + * Outputs: + * *value - receives the value of the address-expression + * *offset - receives the offset specified, if any + * *name - receives the symbol name, if any + * *nextarg - index to next unparsed argument in argv[] + * + * Returns: + * zero is returned on success, a kdb diagnostic code is + * returned on error. + * + * Locking: + * No locking requirements. + * + * Remarks: + * + */ + +int +kdbgetaddrarg(int argc, const char **argv, int *nextarg, + kdb_machreg_t *value, long *offset, + char **name) +{ + kdb_machreg_t addr; + unsigned long off = 0; + int positive; + int diag; + int found = 0; + char *symname; + char symbol = '\0'; + char *cp; + kdb_symtab_t symtab; + + /* + * Process arguments which follow the following syntax: + * + * symbol | numeric-address [+/- numeric-offset] + * %register + * $environment-variable + */ + + if (*nextarg > argc) { + return KDB_ARGCOUNT; + } + + symname = (char *)argv[*nextarg]; + + /* + * If there is no whitespace between the symbol + * or address and the '+' or '-' symbols, we + * remember the character and replace it with a + * null so the symbol/value can be properly parsed + */ + if ((cp = strpbrk(symname, "+-")) != NULL) { + symbol = *cp; + *cp++ = '\0'; + } + + if (symname[0] == '$') { + diag = kdbgetulenv(&symname[1], &addr); + if (diag) + return diag; + } else if (symname[0] == '%') { + if ((diag = kdb_check_regs())) + return diag; + diag = kdba_getregcontents(&symname[1], kdb_current_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) +{ + int i; + + if (diag >= 0) { + kdb_printf("no error detected (diagnostic is %d)\n", diag); + return; + } + + for(i=0; i<__nkdb_err; i++) { + if (kdbmsgs[i].km_diag == diag) { + kdb_printf("diag: %d: %s\n", diag, kdbmsgs[i].km_msg); + return; + } + } + + kdb_printf("Unknown diag %d\n", -diag); +} + +/* + * kdb_defcmd, kdb_defcmd2 + * + * This function implements the 'defcmd' command which defines one + * command as a set of other commands, terminated by endefcmd. + * kdb_defcmd processes the initial 'defcmd' command, kdb_defcmd2 + * is invoked from kdb_parse for the following commands until + * 'endefcmd'. + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +struct defcmd_set { + int count; + int usable; + char *name; + char *usage; + char *help; + char **command; +}; +static struct defcmd_set *defcmd_set; +static int defcmd_set_count; +static int defcmd_in_progress; + +/* Forward references */ +static int kdb_exec_defcmd(int argc, const char **argv); + +static int +kdb_defcmd2(const char *cmdstr, const char *argv0) +{ + struct defcmd_set *s = defcmd_set + defcmd_set_count - 1; + char **save_command = s->command; + if (strcmp(argv0, "endefcmd") == 0) { + defcmd_in_progress = 0; + if (!s->count) + s->usable = 0; + if (s->usable) + kdb_register(s->name, kdb_exec_defcmd, s->usage, s->help, 0); + return 0; + } + if (!s->usable) + return KDB_NOTIMP; + s->command = kmalloc((s->count + 1) * sizeof(*(s->command)), GFP_KDB); + if (!s->command) { + kdb_printf("Could not allocate new kdb_defcmd table for %s\n", cmdstr); + s->usable = 0; + return KDB_NOTIMP; + } + memcpy(s->command, save_command, s->count * sizeof(*(s->command))); + s->command[s->count++] = kdb_strdup(cmdstr, GFP_KDB); + kfree(save_command); + return 0; +} + +static int +kdb_defcmd(int argc, const char **argv) +{ + struct defcmd_set *save_defcmd_set = defcmd_set, *s; + if (defcmd_in_progress) { + kdb_printf("kdb: nested defcmd detected, assuming missing endefcmd\n"); + kdb_defcmd2("endefcmd", "endefcmd"); + } + if (argc == 0) { + int i; + for (s = defcmd_set; s < defcmd_set + defcmd_set_count; ++s) { + kdb_printf("defcmd %s \"%s\" \"%s\"\n", s->name, s->usage, s->help); + for (i = 0; i < s->count; ++i) + kdb_printf("%s", s->command[i]); + kdb_printf("endefcmd\n"); + } + return 0; + } + if (argc != 3) + return KDB_ARGCOUNT; + defcmd_set = kmalloc((defcmd_set_count + 1) * sizeof(*defcmd_set), GFP_KDB); + if (!defcmd_set) { + kdb_printf("Could not allocate new defcmd_set entry for %s\n", argv[1]); + defcmd_set = save_defcmd_set; + return KDB_NOTIMP; + } + memcpy(defcmd_set, save_defcmd_set, defcmd_set_count * sizeof(*defcmd_set)); + kfree(save_defcmd_set); + s = defcmd_set + defcmd_set_count; + memset(s, 0, sizeof(*s)); + s->usable = 1; + s->name = kdb_strdup(argv[1], GFP_KDB); + s->usage = kdb_strdup(argv[2], GFP_KDB); + s->help = kdb_strdup(argv[3], GFP_KDB); + if (s->usage[0] == '"') { + strcpy(s->usage, s->usage+1); + s->usage[strlen(s->usage)-1] = '\0'; + } + if (s->help[0] == '"') { + strcpy(s->help, s->help+1); + s->help[strlen(s->help)-1] = '\0'; + } + ++defcmd_set_count; + defcmd_in_progress = 1; + return 0; +} + +/* + * kdb_exec_defcmd + * + * Execute the set of commands associated with this defcmd name. + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +static int +kdb_exec_defcmd(int argc, const char **argv) +{ + int i, ret; + struct defcmd_set *s; + if (argc != 0) + return KDB_ARGCOUNT; + for (s = defcmd_set, i = 0; i < defcmd_set_count; ++i, ++s) { + if (strcmp(s->name, argv[0]) == 0) + break; + } + if (i == defcmd_set_count) { + kdb_printf("kdb_exec_defcmd: could not find commands for %s\n", argv[0]); + return KDB_NOTIMP; + } + for (i = 0; i < s->count; ++i) { + /* Recursive use of kdb_parse, do not use argv after this point */ + argv = NULL; + kdb_printf("[%s]kdb> %s\n", s->name, s->command[i]); + if ((ret = kdb_parse(s->command[i]))) + return ret; + } + return 0; +} + +/* Command history */ +#define KDB_CMD_HISTORY_COUNT 32 +#define CMD_BUFLEN 200 /* kdb_printf: max printline size == 256 */ +static unsigned int cmd_head=0, cmd_tail=0; +static unsigned int cmdptr; +static char cmd_hist[KDB_CMD_HISTORY_COUNT][CMD_BUFLEN]; +static char cmd_cur[CMD_BUFLEN]; + +/* + * The "str" argument may point to something like | grep xyz + * + */ +static void +parse_grep(const char *str) +{ + int len; + char *cp = (char *)str, *cp2; + + /* sanity check: we should have been called with the \ first */ + if (*cp != '|') + return; + cp++; + while (isspace(*cp)) cp++; + if (strncmp(cp,"grep ",5)) { + kdb_printf ("invalid 'pipe', see grephelp\n"); + return; + } + cp += 5; + while (isspace(*cp)) cp++; + cp2 = strchr(cp, '\n'); + if (cp2) + *cp2 = '\0'; /* remove the trailing newline */ + len = strlen(cp); + if (len == 0) { + kdb_printf ("invalid 'pipe', see grephelp\n"); + return; + } + /* now cp points to a nonzero length search string */ + if (*cp == '"') { + /* allow it be "x y z" by removing the "'s - there must + be two of them */ + cp++; + cp2 = strchr(cp, '"'); + if (!cp2) { + kdb_printf ("invalid quoted string, see grephelp\n"); + return; + } + *cp2 = '\0'; /* end the string where the 2nd " was */ + } + kdb_grep_leading = 0; + if (*cp == '^') { + kdb_grep_leading = 1; + cp++; + } + len = strlen(cp); + kdb_grep_trailing = 0; + if (*(cp+len-1) == '$') { + kdb_grep_trailing = 1; + *(cp+len-1) = '\0'; + } + len = strlen(cp); + if (!len) return; + if (len >= GREP_LEN) { + kdb_printf ("search string too long\n"); + return; + } + strcpy(kdb_grep_string, cp); + kdb_grepping_flag++; + return; +} + +/* + * kdb_parse + * + * Parse the command line, search the command table for a + * matching command and invoke the command function. + * This function may be called recursively, if it is, the second call + * will overwrite argv and cbuf. It is the caller's responsibility to + * save their argv if they recursively call kdb_parse(). + * + * 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 + +int +kdb_parse(const char *cmdstr) +{ + static char *argv[MAXARGC]; + static int argc = 0; + static char cbuf[CMD_BUFLEN+2]; + char *cp; + char *cpp, quoted; + kdbtab_t *tp; + int i, escaped, ignore_errors = 0, check_grep; + + /* + * First tokenize the command string. + */ + cp = (char *)cmdstr; + kdb_grepping_flag = check_grep = 0; + + if (KDB_FLAG(CMD_INTERRUPT)) { + /* Previous command was interrupted, newline must not repeat the command */ + KDB_FLAG_CLEAR(CMD_INTERRUPT); + argc = 0; /* no repeat */ + } + + if (*cp != '\n' && *cp != '\0') { + argc = 0; + cpp = cbuf; + while (*cp) { + /* skip whitespace */ + while (isspace(*cp)) cp++; + if ((*cp == '\0') || (*cp == '\n') || (*cp == '#' && !defcmd_in_progress)) + break; + /* special case: check for | grep pattern */ + if (*cp == '|') { + check_grep++; + break; + } + if (cpp >= cbuf + CMD_BUFLEN) { + kdb_printf("kdb_parse: command buffer overflow, command ignored\n%s\n", cmdstr); + return KDB_NOTFOUND; + } + if (argc >= MAXARGC - 1) { + kdb_printf("kdb_parse: too many arguments, command ignored\n%s\n", cmdstr); + return KDB_NOTFOUND; + } + argv[argc++] = cpp; + escaped = 0; + quoted = '\0'; + /* Copy to next unquoted and unescaped whitespace or '=' */ + while (*cp && *cp != '\n' && (escaped || quoted || !isspace(*cp))) { + if (cpp >= cbuf + CMD_BUFLEN) + break; + if (escaped) { + escaped = 0; + *cpp++ = *cp++; + continue; + } + if (*cp == '\\') { + escaped = 1; + ++cp; + continue; + } + if (*cp == quoted) { + quoted = '\0'; + } else if (*cp == '\'' || *cp == '"') { + quoted = *cp; + } + if ((*cpp = *cp++) == '=' && !quoted) + break; + ++cpp; + } + *cpp++ = '\0'; /* Squash a ws or '=' character */ + } + } + if (!argc) + return 0; + if (check_grep) + parse_grep(cp); + if (defcmd_in_progress) { + int result = kdb_defcmd2(cmdstr, argv[0]); + if (!defcmd_in_progress) { + argc = 0; /* avoid repeat on endefcmd */ + *(argv[0]) = '\0'; + } + return result; + } + if (argv[0][0] == '-' && argv[0][1] && (argv[0][1] < '0' || argv[0][1] > '9')) { + ignore_errors = 1; + ++argv[0]; + } + + for(tp=kdb_commands, i=0; i < kdb_max_commands; i++,tp++) { + if (tp->cmd_name) { + /* + * If this command is allowed to be abbreviated, + * check to see if this is it. + */ + + if (tp->cmd_minlen + && (strlen(argv[0]) <= tp->cmd_minlen)) { + if (strncmp(argv[0], + tp->cmd_name, + tp->cmd_minlen) == 0) { + break; + } + } + + if (strcmp(argv[0], tp->cmd_name)==0) { + break; + } + } + } + + /* + * If we don't find a command by this name, see if the first + * few characters of this match any of the known commands. + * e.g., md1c20 should match md. + */ + if (i == kdb_max_commands) { + for(tp=kdb_commands, i=0; i < kdb_max_commands; i++,tp++) { + if (tp->cmd_name) { + if (strncmp(argv[0], + tp->cmd_name, + strlen(tp->cmd_name))==0) { + break; + } + } + } + } + + if (i < kdb_max_commands) { + int result; + KDB_STATE_SET(CMD); + result = (*tp->cmd_func)(argc-1, + (const char**)argv); + if (result && ignore_errors && result > KDB_CMD_GO) + result = 0; + KDB_STATE_CLEAR(CMD); + switch (tp->cmd_repeat) { + case KDB_REPEAT_NONE: + argc = 0; + if (argv[0]) + *(argv[0]) = '\0'; + break; + case KDB_REPEAT_NO_ARGS: + argc = 1; + if (argv[1]) + *(argv[1]) = '\0'; + break; + case KDB_REPEAT_WITH_ARGS: + break; + } + return result; + } + + /* + * If the input with which we were presented does not + * map to an existing command, attempt to parse it as an + * address argument and display the result. Useful for + * obtaining the address of a variable, or the nearest symbol + * to an address contained in a register. + */ + { + kdb_machreg_t value; + char *name = NULL; + long offset; + int nextarg = 0; + + if (kdbgetaddrarg(0, (const char **)argv, &nextarg, + &value, &offset, &name)) { + return KDB_NOTFOUND; + } + + kdb_printf("%s = ", argv[0]); + kdb_symbol_print(value, NULL, KDB_SP_DEFAULT); + kdb_printf("\n"); + return 0; + } +} + + +static int +handle_ctrl_cmd(char *cmd) +{ +#define CTRL_P 16 +#define CTRL_N 14 + + /* initial situation */ + if (cmd_head == cmd_tail) return 0; + + switch(*cmd) { + case CTRL_P: + if (cmdptr != cmd_tail) + cmdptr = (cmdptr-1) % KDB_CMD_HISTORY_COUNT; + strncpy(cmd_cur, cmd_hist[cmdptr], CMD_BUFLEN); + return 1; + case CTRL_N: + if (cmdptr != cmd_head) + cmdptr = (cmdptr+1) % KDB_CMD_HISTORY_COUNT; + strncpy(cmd_cur, cmd_hist[cmdptr], CMD_BUFLEN); + return 1; + } + return 0; +} + +/* + * kdb_do_dump + * + * Call the dump() function if the kernel is configured for LKCD. + * Inputs: + * None. + * Outputs: + * None. + * Returns: + * None. dump() may or may not return. + * Locking: + * none. + * Remarks: + */ + +static void +kdb_do_dump(void) +{ +#if defined(CONFIG_LKCD_DUMP) || defined(CONFIG_LKCD_DUMP_MODULE) + kdb_printf("Forcing dump (if configured)\n"); + console_loglevel = 8; /* to see the dump messages */ + dump("kdb_do_dump"); +#endif +} + +/* + * kdb_reboot + * + * This function implements the 'reboot' command. Reboot the system + * immediately. + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Shouldn't return from this function. + */ + +static int +kdb_reboot(int argc, const char **argv) +{ + emergency_restart(); + kdb_printf("Hmm, kdb_reboot did not reboot, spinning here\n"); + while (1) {}; + /* NOTREACHED */ + return 0; +} + +#ifdef CONFIG_KDB_KDUMP + +int kdb_kdump_state = KDB_KDUMP_RESET; /* KDB kdump state */ + +static int kdb_cpu(int argc, const char **argv); + +/* + * kdb_kdump_check + * + * This is where the kdump on monarch cpu is handled. + * + */ +void kdb_kdump_check(struct pt_regs *regs) +{ + if (kdb_kdump_state != KDB_KDUMP_RESET) { + crash_kexec(regs); + + /* If the call above returned then something + didn't work */ + kdb_printf("kdb_kdump_check: crash_kexec failed!\n"); + kdb_printf(" Please check if the kdump kernel has been properly loaded\n"); + kdb_kdump_state = KDB_KDUMP_RESET; + } +} + + +/* + * kdb_kdump + * + * This function implements the 'kdump' command. + * + * 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. + */ + +static int +kdb_kdump(int argc, const char **argv) +{ + char cpu_id[6]; /* up to 99,999 cpus */ + const char *cpu_argv[] = {NULL, cpu_id, NULL}; + int ret; + + kdb_kdump_state = KDB_KDUMP_KDUMP; + /* Switch back to the initial cpu before process kdump command */ + if (smp_processor_id() != kdb_initial_cpu) { + sprintf(cpu_id, "%d", kdb_initial_cpu); + ret = kdb_cpu(1, cpu_argv); + if (ret != KDB_CMD_CPU) { + kdb_printf("kdump: Failed to switch to initial cpu %d;" + " aborted\n", kdb_initial_cpu); + kdb_kdump_state = KDB_KDUMP_RESET; + } + } else + ret = KDB_CMD_CPU; + + return ret; +} + +#endif /* CONFIG_KDB_KDUMP */ + +static int +kdb_quiet(int reason) +{ + return (reason == KDB_REASON_CPU_UP || reason == KDB_REASON_SILENT); +} + +/* + * kdb_local + * + * The main code for kdb. This routine is invoked on a specific + * processor, it is not global. The main kdb() routine ensures + * that only one processor at a time is in this routine. This + * code is called with the real reason code on the first entry + * to a kdb session, thereafter it is called with reason SWITCH, + * even if the user goes back to the original cpu. + * + * Inputs: + * reason The reason KDB was invoked + * error The hardware-defined error code + * regs The exception frame at time of fault/breakpoint. NULL + * for reason SILENT or CPU_UP, otherwise valid. + * db_result Result code from the break or debug point. + * Returns: + * 0 KDB was invoked for an event which it wasn't responsible + * 1 KDB handled the event for which it was invoked. + * KDB_CMD_GO User typed 'go'. + * KDB_CMD_CPU User switched to another cpu. + * KDB_CMD_SS Single step. + * KDB_CMD_SSB Single step until branch. + * Locking: + * none + * Remarks: + * none + */ + +static int +kdb_local(kdb_reason_t reason, int error, struct pt_regs *regs, kdb_dbtrap_t db_result) +{ + char *cmdbuf; + int diag; + struct task_struct *kdb_current = kdb_curr_task(smp_processor_id()); + +#ifdef CONFIG_KDB_KDUMP + kdb_kdump_check(regs); +#endif + + /* If kdb has been entered for an event which has been/will be + * recovered then silently return. We have to get this far into kdb in + * order to synchronize all the cpus, typically only one cpu (monarch) + * knows that the event is recoverable but the other cpus (slaves) may + * also be driven into kdb before that decision is made by the monarch. + * + * To pause in kdb even for recoverable events, 'set RECOVERY_PAUSE 1' + */ + KDB_DEBUG_STATE("kdb_local 1", reason); + if (reason == KDB_REASON_ENTER + && KDB_FLAG(RECOVERY) + && !KDB_FLAG(CATASTROPHIC)) { + int recovery_pause = 0; + kdbgetintenv("RECOVERY_PAUSE", &recovery_pause); + if (recovery_pause == 0) + reason = KDB_REASON_SILENT; + else + kdb_printf("%s: Recoverable error detected but" + " RECOVERY_PAUSE is set, staying in KDB\n", + __FUNCTION__); + } + + KDB_DEBUG_STATE("kdb_local 2", reason); + kdb_go_count = 0; + if (kdb_quiet(reason)) { + /* no message */ + } else if (reason == KDB_REASON_DEBUG) { + /* special case below */ + } else { + kdb_printf("\nEntering kdb (current=0x%p, pid %d) ", kdb_current, kdb_current->pid); +#if defined(CONFIG_SMP) + kdb_printf("on processor %d ", smp_processor_id()); +#endif + } + + switch (reason) { + case KDB_REASON_DEBUG: + { + /* + * If re-entering kdb after a single step + * command, don't print the message. + */ + switch(db_result) { + case KDB_DB_BPT: + kdb_printf("\nEntering kdb (0x%p, pid %d) ", kdb_current, kdb_current->pid); +#if defined(CONFIG_SMP) + kdb_printf("on processor %d ", smp_processor_id()); +#endif + kdb_printf("due to Debug @ " kdb_machreg_fmt "\n", kdba_getpc(regs)); + break; + case KDB_DB_SSB: + /* + * In the midst of ssb command. Just return. + */ + KDB_DEBUG_STATE("kdb_local 3", reason); + return KDB_CMD_SSB; /* Continue with SSB command */ + + break; + case KDB_DB_SS: + break; + case KDB_DB_SSBPT: + KDB_DEBUG_STATE("kdb_local 4", reason); + return 1; /* kdba_db_trap did the work */ + default: + kdb_printf("kdb: Bad result from kdba_db_trap: %d\n", + db_result); + break; + } + + } + break; + case KDB_REASON_ENTER: + if (KDB_STATE(KEYBOARD)) + kdb_printf("due to Keyboard Entry\n"); + else { + kdb_printf("due to KDB_ENTER()\n"); + } + break; + case KDB_REASON_KEYBOARD: + KDB_STATE_SET(KEYBOARD); + kdb_printf("due to Keyboard Entry\n"); + break; + case KDB_REASON_ENTER_SLAVE: /* drop through, slaves only get released via cpu switch */ + case KDB_REASON_SWITCH: + kdb_printf("due to cpu switch\n"); + if (KDB_STATE(GO_SWITCH)) { + KDB_STATE_CLEAR(GO_SWITCH); + KDB_DEBUG_STATE("kdb_local 5", reason); + return KDB_CMD_GO; + } + break; + case KDB_REASON_OOPS: + kdb_printf("Oops: %s\n", kdb_diemsg); + kdb_printf("due to oops @ " kdb_machreg_fmt "\n", kdba_getpc(regs)); + kdba_dumpregs(regs, NULL, NULL); + break; + case KDB_REASON_NMI: + kdb_printf("due to NonMaskable Interrupt @ " kdb_machreg_fmt "\n", + kdba_getpc(regs)); + kdba_dumpregs(regs, NULL, NULL); + break; + case KDB_REASON_BREAK: + kdb_printf("due to Breakpoint @ " kdb_machreg_fmt "\n", kdba_getpc(regs)); + /* + * Determine if this breakpoint is one that we + * are interested in. + */ + if (db_result != KDB_DB_BPT) { + kdb_printf("kdb: error return from kdba_bp_trap: %d\n", db_result); + KDB_DEBUG_STATE("kdb_local 6", reason); + return 0; /* Not for us, dismiss it */ + } + break; + case KDB_REASON_RECURSE: + kdb_printf("due to Recursion @ " kdb_machreg_fmt "\n", kdba_getpc(regs)); + break; + case KDB_REASON_CPU_UP: + case KDB_REASON_SILENT: + KDB_DEBUG_STATE("kdb_local 7", reason); + if (reason == KDB_REASON_CPU_UP) + kdba_cpu_up(); + return KDB_CMD_GO; /* Silent entry, silent exit */ + break; + default: + kdb_printf("kdb: unexpected reason code: %d\n", reason); + KDB_DEBUG_STATE("kdb_local 8", reason); + return 0; /* Not for us, dismiss it */ + } + + kdba_local_arch_setup(); + + kdba_set_current_task(kdb_current); + + while (1) { + /* + * Initialize pager context. + */ + kdb_nextline = 1; + KDB_STATE_CLEAR(SUPPRESS); +#ifdef kdba_setjmp + /* + * Use kdba_setjmp/kdba_longjmp to break out of + * the pager early and to attempt to recover from kdb errors. + */ + KDB_STATE_CLEAR(LONGJMP); + if (kdbjmpbuf) { + if (kdba_setjmp(&kdbjmpbuf[smp_processor_id()])) { + /* Command aborted (usually in pager) */ + continue; + } + else + KDB_STATE_SET(LONGJMP); + } +#endif /* kdba_setjmp */ + + cmdbuf = cmd_cur; + *cmdbuf = '\0'; + *(cmd_hist[cmd_head])='\0'; + + if (KDB_FLAG(ONLY_DO_DUMP)) { + /* kdb is off but a catastrophic error requires a dump. + * Take the dump and reboot. + * Turn on logging so the kdb output appears in the log + * buffer in the dump. + */ + const char *setargs[] = { "set", "LOGGING", "1" }; + kdb_set(2, setargs); + kdb_do_dump(); + kdb_reboot(0, NULL); + /*NOTREACHED*/ + } + +do_full_getstr: +#if defined(CONFIG_SMP) + snprintf(kdb_prompt_str, CMD_BUFLEN, kdbgetenv("PROMPT"), smp_processor_id()); +#else + snprintf(kdb_prompt_str, CMD_BUFLEN, kdbgetenv("PROMPT")); +#endif + if (defcmd_in_progress) + strncat(kdb_prompt_str, "[defcmd]", CMD_BUFLEN); + + /* + * Fetch command from keyboard + */ + cmdbuf = kdb_getstr(cmdbuf, CMD_BUFLEN, kdb_prompt_str); + if (*cmdbuf != '\n') { + if (*cmdbuf < 32) { + if(cmdptr == cmd_head) { + strncpy(cmd_hist[cmd_head], cmd_cur, CMD_BUFLEN); + *(cmd_hist[cmd_head]+strlen(cmd_hist[cmd_head])-1) = '\0'; + } + if(!handle_ctrl_cmd(cmdbuf)) + *(cmd_cur+strlen(cmd_cur)-1) = '\0'; + cmdbuf = cmd_cur; + goto do_full_getstr; + } + else + strncpy(cmd_hist[cmd_head], cmd_cur, CMD_BUFLEN); + + cmd_head = (cmd_head+1) % KDB_CMD_HISTORY_COUNT; + if (cmd_head == cmd_tail) cmd_tail = (cmd_tail+1) % KDB_CMD_HISTORY_COUNT; + + } + + cmdptr = cmd_head; + diag = kdb_parse(cmdbuf); + if (diag == KDB_NOTFOUND) { + kdb_printf("Unknown kdb command: '%s'\n", cmdbuf); + diag = 0; + } + if (diag == KDB_CMD_GO + || diag == KDB_CMD_CPU + || diag == KDB_CMD_SS + || diag == KDB_CMD_SSB) + break; + + if (diag) + kdb_cmderror(diag); + } + + kdba_local_arch_cleanup(); + + KDB_DEBUG_STATE("kdb_local 9", diag); + return diag; +} + + +/* + * kdb_print_state + * + * Print the state data for the current processor for debugging. + * + * Inputs: + * text Identifies the debug point + * value Any integer value to be printed, e.g. reason code. + * Returns: + * None. + * Locking: + * none + * Remarks: + * none + */ + +void kdb_print_state(const char *text, int value) +{ + kdb_printf("state: %s cpu %d value %d initial %d state %x\n", + text, smp_processor_id(), value, kdb_initial_cpu, kdb_state[smp_processor_id()]); +} + +/* + * kdb_previous_event + * + * Return a count of cpus that are leaving kdb, i.e. the number + * of processors that are still handling the previous kdb event. + * + * Inputs: + * None. + * Returns: + * Count of cpus in previous event. + * Locking: + * none + * Remarks: + * none + */ + +static int +kdb_previous_event(void) +{ + int i, leaving = 0; + for (i = 0; i < NR_CPUS; ++i) { + if (KDB_STATE_CPU(LEAVING, i)) + ++leaving; + } + return leaving; +} + +/* + * kdb_wait_for_cpus + * + * Invoked once at the start of a kdb event, from the controlling cpu. Wait a + * short period for the other cpus to enter kdb state. + * + * Inputs: + * none + * Returns: + * none + * Locking: + * none + * Remarks: + * none + */ + +int kdb_wait_for_cpus_secs; + +static void +kdb_wait_for_cpus(void) +{ +#ifdef CONFIG_SMP + int online = 0, kdb_data = 0, prev_kdb_data = 0, c, time; + mdelay(100); + for (time = 0; time < kdb_wait_for_cpus_secs; ++time) { + online = 0; + kdb_data = 0; + for_each_online_cpu(c) { + ++online; + if (kdb_running_process[c].seqno >= kdb_seqno - 1) + ++kdb_data; + } + if (online == kdb_data) + break; + if (prev_kdb_data != kdb_data) { + kdb_nextline = 0; /* no prompt yet */ + kdb_printf(" %d out of %d cpus in kdb, waiting for the rest, timeout in %d second(s)\n", + kdb_data, online, kdb_wait_for_cpus_secs - time); + prev_kdb_data = kdb_data; + } + touch_nmi_watchdog(); + mdelay(1000); + /* Architectures may want to send a more forceful interrupt */ + if (time == min(kdb_wait_for_cpus_secs / 2, 5)) + kdba_wait_for_cpus(); + if (time % 4 == 0) + kdb_printf("."); + } + if (time) { + int wait = online - kdb_data; + if (wait == 0) + kdb_printf("All cpus are now in kdb\n"); + else + kdb_printf("%d cpu%s not in kdb, %s state is unknown\n", + wait, + wait == 1 ? " is" : "s are", + wait == 1 ? "its" : "their"); + } + /* give back the vector we took over in smp_kdb_stop */ + kdba_giveback_vector(KDB_VECTOR); +#endif /* CONFIG_SMP */ +} + +/* + * kdb_main_loop + * + * The main kdb loop. After initial setup and assignment of the controlling + * cpu, all cpus are in this loop. One cpu is in control and will issue the kdb + * prompt, the others will spin until 'go' or cpu switch. + * + * To get a consistent view of the kernel stacks for all processes, this routine + * is invoked from the main kdb code via an architecture specific routine. + * kdba_main_loop is responsible for making the kernel stacks consistent for all + * processes, there should be no difference between a blocked process and a + * running process as far as kdb is concerned. + * + * Inputs: + * reason The reason KDB was invoked + * error The hardware-defined error code + * reason2 kdb's current reason code. Initially error but can change + * acording to kdb state. + * db_result Result code from break or debug point. + * regs The exception frame at time of fault/breakpoint. If reason + * is SILENT or CPU_UP then regs is NULL, otherwise it + * should always be valid. + * Returns: + * 0 KDB was invoked for an event which it wasn't responsible + * 1 KDB handled the event for which it was invoked. + * Locking: + * none + * Remarks: + * none + */ + +int +kdb_main_loop(kdb_reason_t reason, kdb_reason_t reason2, int error, + kdb_dbtrap_t db_result, struct pt_regs *regs) +{ + int result = 1; + /* Stay in kdb() until 'go', 'ss[b]' or an error */ + while (1) { + /* + * All processors except the one that is in control + * will spin here. + */ + KDB_DEBUG_STATE("kdb_main_loop 1", reason); + while (KDB_STATE(HOLD_CPU)) { + /* state KDB is turned off by kdb_cpu to see if the + * other cpus are still live, each cpu in this loop + * turns it back on. + */ + if (!KDB_STATE(KDB)) { + KDB_STATE_SET(KDB); + } + +#if defined(CONFIG_KDB_KDUMP) + if (KDB_STATE(KEXEC)) { + struct pt_regs r; + if (regs == NULL) + regs = &r; + + kdba_kdump_shutdown_slave(regs); + return 0; + } +#endif + } + + KDB_STATE_CLEAR(SUPPRESS); + KDB_DEBUG_STATE("kdb_main_loop 2", reason); + if (KDB_STATE(LEAVING)) + break; /* Another cpu said 'go' */ + + if (!kdb_quiet(reason)) + kdb_wait_for_cpus(); + /* Still using kdb, this processor is in control */ + result = kdb_local(reason2, error, regs, db_result); + KDB_DEBUG_STATE("kdb_main_loop 3", result); + + if (result == KDB_CMD_CPU) { + /* Cpu switch, hold the current cpu, release the target one. */ + reason2 = KDB_REASON_SWITCH; + KDB_STATE_SET(HOLD_CPU); + KDB_STATE_CLEAR_CPU(HOLD_CPU, kdb_new_cpu); + continue; + } + + if (result == KDB_CMD_SS) { + KDB_STATE_SET(DOING_SS); + break; + } + + if (result == KDB_CMD_SSB) { + KDB_STATE_SET(DOING_SS); + KDB_STATE_SET(DOING_SSB); + break; + } + + if (result && result != 1 && result != KDB_CMD_GO) + kdb_printf("\nUnexpected kdb_local return code %d\n", result); + + KDB_DEBUG_STATE("kdb_main_loop 4", reason); + break; + } + if (KDB_STATE(DOING_SS)) + KDB_STATE_CLEAR(SSBPT); + return result; +} + +/* iapc_boot_arch was defined in ACPI 2.0, FADT revision 3 onwards. For any + * FADT prior to revision 3, we have to assume that we have an i8042 I/O + * device. ACPI initialises after KDB initialises but before using KDB, so + * check iapc_boot_arch on each entry to KDB. + */ +static void +kdb_check_i8042(void) +{ + KDB_FLAG_CLEAR(NO_I8042); +#ifdef CONFIG_ACPI + if (acpi_gbl_FADT.header.revision >= 3 && + (acpi_gbl_FADT.boot_flags & ACPI_FADT_8042) == 0) + KDB_FLAG_SET(NO_I8042); +#endif /* CONFIG_ACPI */ +} + +/* + * 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. + * + * The breakpoint trap code should invoke this function with + * one of KDB_REASON_BREAK (int 03) or KDB_REASON_DEBUG (debug register) + * + * the die_if_kernel function should invoke this function with + * KDB_REASON_OOPS. + * + * In single step mode, one cpu is released to run without + * breakpoints. Interrupts and NMI are reset to their original values, + * the cpu is allowed to do one instruction which causes a trap + * into kdb with KDB_REASON_DEBUG. + * + * Inputs: + * reason The reason KDB was invoked + * error The hardware-defined error code + * regs The exception frame at time of fault/breakpoint. If reason + * is SILENT or CPU_UP then regs is NULL, otherwise it + * should always be valid. + * Returns: + * 0 KDB was invoked for an event which it wasn't responsible + * 1 KDB handled the event for which it was invoked. + * Locking: + * none + * Remarks: + * No assumptions of system state. This function may be invoked + * with arbitrary locks held. It will stop all other processors + * in an SMP environment, disable all interrupts and does not use + * the operating systems keyboard driver. + * + * This code is reentrant but only for cpu switch. Any other + * reentrancy is an error, although kdb will attempt to recover. + * + * At the start of a kdb session the initial processor is running + * kdb() and the other processors can be doing anything. When the + * initial processor calls smp_kdb_stop() the other processors are + * driven through kdb_ipi which calls kdb() with reason SWITCH. + * That brings all processors into this routine, one with a "real" + * reason code, the other with SWITCH. + * + * Because the other processors are driven via smp_kdb_stop(), + * they enter here from the NMI handler. Until the other + * processors exit from here and exit from kdb_ipi, they will not + * take any more NMI requests. The initial cpu will still take NMI. + * + * Multiple race and reentrancy conditions, each with different + * advoidance mechanisms. + * + * Two cpus hit debug points at the same time. + * + * kdb_lock and kdb_initial_cpu ensure that only one cpu gets + * control of kdb. The others spin on kdb_initial_cpu until + * they are driven through NMI into kdb_ipi. When the initial + * cpu releases the others from NMI, they resume trying to get + * kdb_initial_cpu to start a new event. + * + * A cpu is released from kdb and starts a new event before the + * original event has completely ended. + * + * kdb_previous_event() prevents any cpu from entering + * kdb_initial_cpu state until the previous event has completely + * ended on all cpus. + * + * An exception occurs inside kdb. + * + * kdb_initial_cpu detects recursive entry to kdb and attempts + * to recover. The recovery uses longjmp() which means that + * recursive calls to kdb never return. Beware of assumptions + * like + * + * ++depth; + * kdb(); + * --depth; + * + * If the kdb call is recursive then longjmp takes over and + * --depth is never executed. + * + * NMI handling. + * + * NMI handling is tricky. The initial cpu is invoked by some kdb event, + * this event could be NMI driven but usually is not. The other cpus are + * driven into kdb() via kdb_ipi which uses NMI so at the start the other + * cpus will not accept NMI. Some operations such as SS release one cpu + * but hold all the others. Releasing a cpu means it drops back to + * whatever it was doing before the kdb event, this means it drops out of + * kdb_ipi and hence out of NMI status. But the software watchdog uses + * NMI and we do not want spurious watchdog calls into kdb. kdba_read() + * resets the watchdog counters in its input polling loop, when a kdb + * command is running it is subject to NMI watchdog events. + * + * Another problem with NMI handling is the NMI used to drive the other + * cpus into kdb cannot be distinguished from the watchdog NMI. State + * flag WAIT_IPI indicates that a cpu is waiting for NMI via kdb_ipi, + * if not set then software NMI is ignored by kdb_ipi. + * + * Cpu switching. + * + * All cpus are in kdb (or they should be), all but one are + * spinning on KDB_STATE(HOLD_CPU). Only one cpu is not in + * HOLD_CPU state, only that cpu can handle commands. + * + * Go command entered. + * + * If necessary, go will switch to the initial cpu first. If the event + * was caused by a software breakpoint (assumed to be global) that + * requires single-step to get over the breakpoint then only release the + * initial cpu, after the initial cpu has single-stepped the breakpoint + * then release the rest of the cpus. If SSBPT is not required then + * release all the cpus at once. + */ + +int +kdb(kdb_reason_t reason, int error, struct pt_regs *regs) +{ + kdb_intstate_t int_state; /* Interrupt state */ + kdb_reason_t reason2 = reason; + int result = 0; /* Default is kdb did not handle it */ + int ss_event, old_regs_saved = 0; + struct pt_regs *old_regs = NULL; + kdb_dbtrap_t db_result=KDB_DB_NOBPT; + preempt_disable(); + atomic_inc(&kdb_event); + + switch(reason) { + case KDB_REASON_OOPS: + case KDB_REASON_NMI: + KDB_FLAG_SET(CATASTROPHIC); /* kernel state is dubious now */ + break; + default: + break; + } + switch(reason) { + case KDB_REASON_ENTER: + case KDB_REASON_ENTER_SLAVE: + case KDB_REASON_BREAK: + case KDB_REASON_DEBUG: + case KDB_REASON_OOPS: + case KDB_REASON_SWITCH: + case KDB_REASON_KEYBOARD: + case KDB_REASON_NMI: + if (regs && regs != get_irq_regs()) { + old_regs = set_irq_regs(regs); + old_regs_saved = 1; + } + break; + default: + break; + } + if (kdb_continue_catastrophic > 2) { + kdb_printf("kdb_continue_catastrophic is out of range, setting to 2\n"); + kdb_continue_catastrophic = 2; + } + if (!kdb_on && KDB_FLAG(CATASTROPHIC) && kdb_continue_catastrophic == 2) { + KDB_FLAG_SET(ONLY_DO_DUMP); + } + if (!kdb_on && !KDB_FLAG(ONLY_DO_DUMP)) + goto out; + + KDB_DEBUG_STATE("kdb 1", reason); + KDB_STATE_CLEAR(SUPPRESS); + + /* Filter out userspace breakpoints first, no point in doing all + * the kdb smp fiddling when it is really a gdb trap. + * Save the single step status first, kdba_db_trap clears ss status. + * kdba_b[dp]_trap sets SSBPT if required. + */ + ss_event = KDB_STATE(DOING_SS) || KDB_STATE(SSBPT); +#ifdef CONFIG_CPU_XSCALE + if ( KDB_STATE(A_XSC_ICH) ) { + /* restore changed I_BIT */ + KDB_STATE_CLEAR(A_XSC_ICH); + kdba_restore_retirq(regs, KDB_STATE(A_XSC_IRQ)); + if ( !ss_event ) { + kdb_printf("Stranger!!! Why IRQ bit is changed====\n"); + } + } +#endif + if (reason == KDB_REASON_BREAK) { + db_result = kdba_bp_trap(regs, error); /* Only call this once */ + } + if (reason == KDB_REASON_DEBUG) { + db_result = kdba_db_trap(regs, error); /* Only call this once */ + } + + if ((reason == KDB_REASON_BREAK || reason == KDB_REASON_DEBUG) + && db_result == KDB_DB_NOBPT) { + KDB_DEBUG_STATE("kdb 2", reason); + goto out; /* Not one of mine */ + } + + /* Turn off single step if it was being used */ + if (ss_event) { + kdba_clearsinglestep(regs); + /* Single step after a breakpoint removes the need for a delayed reinstall */ + if (reason == KDB_REASON_BREAK || reason == KDB_REASON_DEBUG) + KDB_STATE_CLEAR(SSBPT); + } + + /* kdb can validly reenter but only for certain well defined conditions */ + if (reason == KDB_REASON_DEBUG + && !KDB_STATE(HOLD_CPU) + && ss_event) + KDB_STATE_SET(REENTRY); + else + KDB_STATE_CLEAR(REENTRY); + + /* Wait for previous kdb event to completely exit before starting + * a new event. + */ + while (kdb_previous_event()) + ; + KDB_DEBUG_STATE("kdb 3", reason); + + /* + * If kdb is already active, print a message and try to recover. + * If recovery is not possible and recursion is allowed or + * forced recursion without recovery is set then try to recurse + * in kdb. Not guaranteed to work but it makes an attempt at + * debugging the debugger. + */ + if (reason != KDB_REASON_SWITCH && + reason != KDB_REASON_ENTER_SLAVE) { + if (KDB_IS_RUNNING() && !KDB_STATE(REENTRY)) { + int recover = 1; + unsigned long recurse = 0; + kdb_printf("kdb: Debugger re-entered on cpu %d, new reason = %d\n", + smp_processor_id(), reason); + /* Should only re-enter from released cpu */ + + if (KDB_STATE(HOLD_CPU)) { + kdb_printf(" Strange, cpu %d should not be running\n", smp_processor_id()); + recover = 0; + } + if (!KDB_STATE(CMD)) { + kdb_printf(" Not executing a kdb command\n"); + recover = 0; + } + if (!KDB_STATE(LONGJMP)) { + kdb_printf(" No longjmp available for recovery\n"); + recover = 0; + } + kdbgetulenv("RECURSE", &recurse); + if (recurse > 1) { + kdb_printf(" Forced recursion is set\n"); + recover = 0; + } + if (recover) { + kdb_printf(" Attempting to abort command and recover\n"); +#ifdef kdba_setjmp + kdba_longjmp(&kdbjmpbuf[smp_processor_id()], 0); +#endif /* kdba_setjmp */ + } + if (recurse) { + if (KDB_STATE(RECURSE)) { + kdb_printf(" Already in recursive mode\n"); + } else { + kdb_printf(" Attempting recursive mode\n"); + KDB_STATE_SET(RECURSE); + KDB_STATE_SET(REENTRY); + reason2 = KDB_REASON_RECURSE; + recover = 1; + } + } + if (!recover) { + kdb_printf(" Cannot recover, allowing event to proceed\n"); + /*temp*/ + while (KDB_IS_RUNNING()) + cpu_relax(); + goto out; + } + } + } else if (reason == KDB_REASON_SWITCH && !KDB_IS_RUNNING()) { + kdb_printf("kdb: CPU switch without kdb running, I'm confused\n"); + goto out; + } + + /* + * Disable interrupts, breakpoints etc. on this processor + * during kdb command processing + */ + KDB_STATE_SET(KDB); + kdba_disableint(&int_state); + if (!KDB_STATE(KDB_CONTROL)) { + kdb_bp_remove_local(); + KDB_STATE_SET(KDB_CONTROL); + } + + /* + * If not entering the debugger due to CPU switch or single step + * reentry, serialize access here. + * The processors may race getting to this point - if, + * for example, more than one processor hits a breakpoint + * at the same time. We'll serialize access to kdb here - + * other processors will loop here, and the NMI from the stop + * IPI will take them into kdb as switch candidates. Once + * the initial processor releases the debugger, the rest of + * the processors will race for it. + * + * The above describes the normal state of affairs, where two or more + * cpus that are entering kdb at the "same" time are assumed to be for + * separate events. However some processes such as ia64 MCA/INIT will + * drive all the cpus into error processing at the same time. For that + * case, all of the cpus entering kdb at the "same" time are really a + * single event. + * + * That case is handled by the use of KDB_ENTER by one cpu (the + * monarch) and KDB_ENTER_SLAVE on the other cpus (the slaves). + * KDB_ENTER_SLAVE maps to KDB_REASON_ENTER_SLAVE. The slave events + * will be treated as if they had just responded to the kdb IPI, i.e. + * as if they were KDB_REASON_SWITCH. + * + * Because of races across multiple cpus, ENTER_SLAVE can occur before + * the main ENTER. Hold up ENTER_SLAVE here until the main ENTER + * arrives. + */ + + if (reason == KDB_REASON_ENTER_SLAVE) { + spin_lock(&kdb_lock); + while (!KDB_IS_RUNNING()) { + spin_unlock(&kdb_lock); + while (!KDB_IS_RUNNING()) + cpu_relax(); + spin_lock(&kdb_lock); + } + reason = KDB_REASON_SWITCH; + KDB_STATE_SET(HOLD_CPU); + spin_unlock(&kdb_lock); + } + + if (reason == KDB_REASON_SWITCH || KDB_STATE(REENTRY)) + ; /* drop through */ + else { + KDB_DEBUG_STATE("kdb 4", reason); + spin_lock(&kdb_lock); + while (KDB_IS_RUNNING() || kdb_previous_event()) { + spin_unlock(&kdb_lock); + while (KDB_IS_RUNNING() || kdb_previous_event()) + cpu_relax(); + spin_lock(&kdb_lock); + } + KDB_DEBUG_STATE("kdb 5", reason); + + kdb_initial_cpu = smp_processor_id(); + ++kdb_seqno; + spin_unlock(&kdb_lock); + if (!kdb_quiet(reason)) + notify_die(DIE_KDEBUG_ENTER, "KDEBUG ENTER", regs, error, 0, 0); + } + + if (smp_processor_id() == kdb_initial_cpu + && !KDB_STATE(REENTRY)) { + KDB_STATE_CLEAR(HOLD_CPU); + KDB_STATE_CLEAR(WAIT_IPI); + kdb_check_i8042(); + /* + * Remove the global breakpoints. This is only done + * once from the initial processor on initial entry. + */ + if (!kdb_quiet(reason) || smp_processor_id() == 0) + kdb_bp_remove_global(); + + /* + * If SMP, stop other processors. The other processors + * will enter kdb() with KDB_REASON_SWITCH and spin in + * kdb_main_loop(). + */ + KDB_DEBUG_STATE("kdb 6", reason); + if (NR_CPUS > 1 && !kdb_quiet(reason)) { + int i; + for (i = 0; i < NR_CPUS; ++i) { + if (!cpu_online(i)) + continue; + if (i != kdb_initial_cpu) { + KDB_STATE_SET_CPU(HOLD_CPU, i); + KDB_STATE_SET_CPU(WAIT_IPI, i); + } + } + KDB_DEBUG_STATE("kdb 7", reason); + smp_kdb_stop(); + KDB_DEBUG_STATE("kdb 8", reason); + } + } + + if (KDB_STATE(GO1)) { + kdb_bp_remove_global(); /* They were set for single-step purposes */ + KDB_STATE_CLEAR(GO1); + reason = KDB_REASON_SILENT; /* Now silently go */ + } + + /* Set up a consistent set of process stacks before talking to the user */ + KDB_DEBUG_STATE("kdb 9", result); + result = kdba_main_loop(reason, reason2, error, db_result, regs); + reason = reason2; /* back to original event type */ + + KDB_DEBUG_STATE("kdb 10", result); + kdba_adjust_ip(reason, error, regs); + KDB_STATE_CLEAR(LONGJMP); + KDB_DEBUG_STATE("kdb 11", result); + /* go which requires single-step over a breakpoint must only release + * one cpu. + */ + if (result == KDB_CMD_GO && KDB_STATE(SSBPT)) + KDB_STATE_SET(GO1); + + if (smp_processor_id() == kdb_initial_cpu && + !KDB_STATE(DOING_SS) && + !KDB_STATE(RECURSE)) { + /* + * (Re)install the global breakpoints and cleanup the cached + * symbol table. This is only done once from the initial + * processor on go. + */ + KDB_DEBUG_STATE("kdb 12", reason); + if (!kdb_quiet(reason) || smp_processor_id() == 0) { + kdb_bp_install_global(regs); + kdbnearsym_cleanup(); + debug_kusage(); + } + if (!KDB_STATE(GO1)) { + /* + * Release all other cpus which will see KDB_STATE(LEAVING) is set. + */ + int i; + for (i = 0; i < NR_CPUS; ++i) { + if (KDB_STATE_CPU(KDB, i)) + KDB_STATE_SET_CPU(LEAVING, i); + KDB_STATE_CLEAR_CPU(WAIT_IPI, i); + KDB_STATE_CLEAR_CPU(HOLD_CPU, i); + } + /* Wait until all the other processors leave kdb */ + while (kdb_previous_event() != 1) + ; + if (!kdb_quiet(reason)) + notify_die(DIE_KDEBUG_LEAVE, "KDEBUG LEAVE", regs, error, 0, 0); + kdb_initial_cpu = -1; /* release kdb control */ + KDB_DEBUG_STATE("kdb 13", reason); + } + } + + KDB_DEBUG_STATE("kdb 14", result); + kdba_restoreint(&int_state); +#ifdef CONFIG_CPU_XSCALE + if ( smp_processor_id() == kdb_initial_cpu && + ( KDB_STATE(SSBPT) | KDB_STATE(DOING_SS) ) + ) { + kdba_setsinglestep(regs); + // disable IRQ in stack frame + KDB_STATE_SET(A_XSC_ICH); + if ( kdba_disable_retirq(regs) ) { + KDB_STATE_SET(A_XSC_IRQ); + } + else { + KDB_STATE_CLEAR(A_XSC_IRQ); + } + } +#endif + + /* Only do this work if we are really leaving kdb */ + if (!(KDB_STATE(DOING_SS) || KDB_STATE(SSBPT) || KDB_STATE(RECURSE))) { + KDB_DEBUG_STATE("kdb 15", result); + kdb_bp_install_local(regs); + if (old_regs_saved) + set_irq_regs(old_regs); + KDB_STATE_CLEAR(KDB_CONTROL); + } + + KDB_DEBUG_STATE("kdb 16", result); + KDB_FLAG_CLEAR(CATASTROPHIC); + KDB_STATE_CLEAR(IP_ADJUSTED); /* Re-adjust ip next time in */ + KDB_STATE_CLEAR(KEYBOARD); + KDB_STATE_CLEAR(KDB); /* Main kdb state has been cleared */ + KDB_STATE_CLEAR(RECURSE); + KDB_STATE_CLEAR(LEAVING); /* No more kdb work after this */ + KDB_DEBUG_STATE("kdb 17", reason); +out: + atomic_dec(&kdb_event); + preempt_enable(); + return result != 0; +} + +/* + * kdb_mdr + * + * This function implements the guts of the 'mdr' command. + * + * mdr , + * + * Inputs: + * addr Start address + * count Number of bytes + * Outputs: + * None. + * Returns: + * Always 0. Any errors are detected and printed by kdb_getarea. + * Locking: + * none. + * Remarks: + */ + +static int +kdb_mdr(kdb_machreg_t addr, unsigned int count) +{ + unsigned char c; + while (count--) { + if (kdb_getarea(c, addr)) + return 0; + kdb_printf("%02x", c); + addr++; + } + kdb_printf("\n"); + return 0; +} + +/* + * kdb_md + * + * This function implements the 'md', 'md1', 'md2', 'md4', 'md8' + * 'mdr' and 'mds' commands. + * + * md|mds [ [ []]] + * mdWcN [ [ []]] + * where W = is the width (1, 2, 4 or 8) and N is the count. + * for eg., md1c20 reads 20 bytes, 1 at a time. + * mdr , + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +static void +kdb_md_line(const char *fmtstr, kdb_machreg_t addr, + int symbolic, int nosect, int bytesperword, + int num, int repeat, int phys) +{ + /* print just one line of data */ + kdb_symtab_t symtab; + char cbuf[32]; + char *c = cbuf; + int i; + unsigned long word; + + memset(cbuf, '\0', sizeof(cbuf)); + if (phys) + kdb_printf("phys " kdb_machreg_fmt0 " ", addr); + else + kdb_printf(kdb_machreg_fmt0 " ", addr); + + for (i = 0; i < num && repeat--; i++) { + if (phys) { + if (kdb_getphysword(&word, addr, bytesperword)) + break; + } else if (kdb_getword(&word, addr, bytesperword)) + break; + kdb_printf(fmtstr, word); + if (symbolic) + kdbnearsym(word, &symtab); + else + memset(&symtab, 0, sizeof(symtab)); + if (symtab.sym_name) { + kdb_symbol_print(word, &symtab, 0); + if (!nosect) { + kdb_printf("\n"); + kdb_printf(" %s %s " + kdb_machreg_fmt " " kdb_machreg_fmt " " kdb_machreg_fmt, + symtab.mod_name, + symtab.sec_name, + symtab.sec_start, + symtab.sym_start, + symtab.sym_end); + } + addr += bytesperword; + } else { + union { + u64 word; + unsigned char c[8]; + } wc; + unsigned char *cp; +#ifdef __BIG_ENDIAN + cp = wc.c + 8 - bytesperword; +#else + cp = wc.c; +#endif + wc.word = word; +#define printable_char(c) ({unsigned char __c = c; isascii(__c) && isprint(__c) ? __c : '.';}) + switch (bytesperword) { + case 8: + *c++ = printable_char(*cp++); + *c++ = printable_char(*cp++); + *c++ = printable_char(*cp++); + *c++ = printable_char(*cp++); + addr += 4; + case 4: + *c++ = printable_char(*cp++); + *c++ = printable_char(*cp++); + addr += 2; + case 2: + *c++ = printable_char(*cp++); + addr++; + case 1: + *c++ = printable_char(*cp++); + addr++; + break; + } +#undef printable_char + } + } + kdb_printf("%*s %s\n", (int)((num-i)*(2*bytesperword + 1)+1), " ", cbuf); +} + +static int +kdb_md(int argc, const char **argv) +{ + static kdb_machreg_t last_addr; + static int last_radix, last_bytesperword, last_repeat; + int radix = 16, mdcount = 8, bytesperword = KDB_WORD_SIZE, repeat; + int nosect = 0; + char fmtchar, fmtstr[64]; + kdb_machreg_t addr; + unsigned long word; + long offset = 0; + int symbolic = 0; + int valid = 0; + int phys = 0; + + kdbgetintenv("MDCOUNT", &mdcount); + kdbgetintenv("RADIX", &radix); + kdbgetintenv("BYTESPERWORD", &bytesperword); + + /* Assume 'md ' and start with environment values */ + repeat = mdcount * 16 / bytesperword; + + if (strcmp(argv[0], "mdr") == 0) { + if (argc != 2) + return KDB_ARGCOUNT; + valid = 1; + } else if (isdigit(argv[0][2])) { + bytesperword = (int)(argv[0][2] - '0'); + if (bytesperword == 0) { + bytesperword = last_bytesperword; + if (bytesperword == 0) { + bytesperword = 4; + } + } + last_bytesperword = bytesperword; + repeat = mdcount * 16 / bytesperword; + if (!argv[0][3]) + valid = 1; + else if (argv[0][3] == 'c' && argv[0][4]) { + char *p; + repeat = simple_strtoul(argv[0]+4, &p, 10); + mdcount = ((repeat * bytesperword) + 15) / 16; + valid = !*p; + } + last_repeat = repeat; + } else if (strcmp(argv[0], "md") == 0) + valid = 1; + else if (strcmp(argv[0], "mds") == 0) + valid = 1; + else if (strcmp(argv[0], "mdp") == 0) { + phys = valid = 1; + } + if (!valid) + return KDB_NOTFOUND; + + if (argc == 0) { + if (last_addr == 0) + return KDB_ARGCOUNT; + addr = last_addr; + radix = last_radix; + bytesperword = last_bytesperword; + repeat = last_repeat; + mdcount = ((repeat * bytesperword) + 15) / 16; + } + + if (argc) { + kdb_machreg_t val; + int diag, nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); + if (diag) + return diag; + if (argc > nextarg+2) + return KDB_ARGCOUNT; + + if (argc >= nextarg) { + diag = kdbgetularg(argv[nextarg], &val); + if (!diag) { + mdcount = (int) val; + repeat = mdcount * 16 / bytesperword; + } + } + if (argc >= nextarg+1) { + diag = kdbgetularg(argv[nextarg+1], &val); + if (!diag) + radix = (int) val; + } + } + + if (strcmp(argv[0], "mdr") == 0) { + return kdb_mdr(addr, mdcount); + } + + switch (radix) { + case 10: + fmtchar = 'd'; + break; + case 16: + fmtchar = 'x'; + break; + case 8: + fmtchar = 'o'; + break; + default: + return KDB_BADRADIX; + } + + last_radix = radix; + + if (bytesperword > KDB_WORD_SIZE) + return KDB_BADWIDTH; + + switch (bytesperword) { + case 8: + sprintf(fmtstr, "%%16.16l%c ", fmtchar); + break; + case 4: + sprintf(fmtstr, "%%8.8l%c ", fmtchar); + break; + case 2: + sprintf(fmtstr, "%%4.4l%c ", fmtchar); + break; + case 1: + sprintf(fmtstr, "%%2.2l%c ", fmtchar); + break; + default: + return KDB_BADWIDTH; + } + + last_repeat = repeat; + last_bytesperword = bytesperword; + + if (strcmp(argv[0], "mds") == 0) { + symbolic = 1; + /* Do not save these changes as last_*, they are temporary mds + * overrides. + */ + bytesperword = KDB_WORD_SIZE; + repeat = mdcount; + kdbgetintenv("NOSECT", &nosect); + } + + /* Round address down modulo BYTESPERWORD */ + + addr &= ~(bytesperword-1); + + while (repeat > 0) { + unsigned long a; + int n, z, num = (symbolic ? 1 : (16 / bytesperword)); + + for (a = addr, z = 0; z < repeat; a += bytesperword, ++z) { + if (phys) { + if (kdb_getphysword(&word, a, bytesperword) + || word) + break; + } else if (kdb_getword(&word, a, bytesperword) || word) + break; + } + n = min(num, repeat); + kdb_md_line(fmtstr, addr, symbolic, nosect, bytesperword, num, repeat, phys); + addr += bytesperword * n; + repeat -= n; + z = (z + num - 1) / num; + if (z > 2) { + int s = num * (z-2); + kdb_printf(kdb_machreg_fmt0 "-" kdb_machreg_fmt0 " zero suppressed\n", + addr, addr + bytesperword * s - 1); + addr += bytesperword * s; + repeat -= s; + } + } + last_addr = addr; + + return 0; +} + +/* + * kdb_mm + * + * This function implements the 'mm' command. + * + * mm address-expression new-value + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * mm works on machine words, mmW works on bytes. + */ + +static int +kdb_mm(int argc, const char **argv) +{ + int diag; + kdb_machreg_t addr; + long offset = 0; + unsigned long contents; + int nextarg; + int width; + + if (argv[0][2] && !isdigit(argv[0][2])) + return KDB_NOTFOUND; + + if (argc < 2) { + return KDB_ARGCOUNT; + } + + nextarg = 1; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL))) + return diag; + + if (nextarg > argc) + return KDB_ARGCOUNT; + + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &contents, NULL, NULL))) + return diag; + + if (nextarg != argc + 1) + return KDB_ARGCOUNT; + + width = argv[0][2] ? (argv[0][2] - '0') : (KDB_WORD_SIZE); + if ((diag = kdb_putword(addr, contents, width))) + return diag; + + kdb_printf(kdb_machreg_fmt " = " kdb_machreg_fmt "\n", addr, contents); + + return 0; +} + +/* + * kdb_go + * + * This function implements the 'go' command. + * + * go [address-expression] + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * KDB_CMD_GO for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +static int +kdb_go(int argc, const char **argv) +{ + kdb_machreg_t addr; + int diag; + int nextarg; + long offset; + struct pt_regs *regs = get_irq_regs(); + + if (argc == 1) { + if (smp_processor_id() != kdb_initial_cpu) { + kdb_printf("go
must be issued from the initial cpu, do cpu %d first\n", kdb_initial_cpu); + return KDB_ARGCOUNT; + } + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, + &addr, &offset, NULL); + if (diag) + return diag; + + kdba_setpc(regs, addr); + } else if (argc) + return KDB_ARGCOUNT; + + diag = KDB_CMD_GO; + if (KDB_FLAG(CATASTROPHIC)) { + kdb_printf("Catastrophic error detected\n"); + kdb_printf("kdb_continue_catastrophic=%d, ", + kdb_continue_catastrophic); + if (kdb_continue_catastrophic == 0 && kdb_go_count++ == 0) { + kdb_printf("type go a second time if you really want to continue\n"); + return 0; + } + if (kdb_continue_catastrophic == 2) { + kdb_do_dump(); + kdb_printf("forcing reboot\n"); + kdb_reboot(0, NULL); + } + kdb_printf("attempting to continue\n"); + } + if (smp_processor_id() != kdb_initial_cpu) { + char buf[80]; + kdb_printf("go was not issued from initial cpu, switching back to cpu %d\n", kdb_initial_cpu); + sprintf(buf, "cpu %d\n", kdb_initial_cpu); + /* Recursive use of kdb_parse, do not use argv after this point */ + argv = NULL; + diag = kdb_parse(buf); + if (diag == KDB_CMD_CPU) + KDB_STATE_SET_CPU(GO_SWITCH, kdb_initial_cpu); + } + return diag; +} + +/* + * 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 + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +static int +kdb_rd(int argc, const char **argv) +{ + int diag; + if (argc == 0) { + if ((diag = kdb_check_regs())) + return diag; + return kdba_dumpregs(kdb_current_regs, NULL, NULL); + } + + if (argc > 2) { + return KDB_ARGCOUNT; + } + + if ((diag = kdb_check_regs())) + return diag; + return kdba_dumpregs(kdb_current_regs, argv[1], argc==2 ? argv[2]: NULL); +} + +/* + * kdb_rm + * + * This function implements the 'rm' (register modify) command. + * + * rm register-name new-contents + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Currently doesn't allow modification of control or + * debug registers. + */ + +static int +kdb_rm(int argc, const char **argv) +{ + int diag; + int ind = 0; + kdb_machreg_t contents; + + if (argc != 2) { + return KDB_ARGCOUNT; + } + + /* + * Allow presence or absence of leading '%' symbol. + */ + + if (argv[1][0] == '%') + ind = 1; + + diag = kdbgetularg(argv[2], &contents); + if (diag) + return diag; + + if ((diag = kdb_check_regs())) + return diag; + diag = kdba_setregcontents(&argv[1][ind], kdb_current_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 + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * None. + */ +static int +kdb_sr(int argc, const char **argv) +{ + extern int __sysrq_enabled; + if (argc != 1) { + return KDB_ARGCOUNT; + } + if (!__sysrq_enabled) { + kdb_printf("Auto activating sysrq\n"); + __sysrq_enabled = 1; + } + + handle_sysrq(*argv[1], NULL); + + return 0; +} +#endif /* CONFIG_MAGIC_SYSRQ */ + +/* + * kdb_ef + * + * This function implements the 'regs' (display exception frame) + * command. This command takes an address and expects to find + * an exception frame at that address, formats and prints it. + * + * regs address-expression + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * Not done yet. + */ + +static int +kdb_ef(int argc, const char **argv) +{ + int diag; + kdb_machreg_t addr; + long offset; + int nextarg; + + if (argc == 1) { + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); + if (diag) + return diag; + + return kdba_dumpregs((struct pt_regs *)addr, NULL, NULL); + } + + return KDB_ARGCOUNT; +} + +#if defined(CONFIG_MODULES) +extern struct list_head *kdb_modules; +extern void free_module(struct module *); + +/* modules using other modules */ +struct module_use +{ + struct list_head list; + struct module *module_which_uses; +}; + +/* + * kdb_lsmod + * + * This function implements the 'lsmod' command. Lists currently + * loaded kernel modules. + * + * Mostly taken from userland lsmod. + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * + */ + +static int +kdb_lsmod(int argc, const char **argv) +{ + struct module *mod; + + if (argc != 0) + return KDB_ARGCOUNT; + + kdb_printf("Module Size modstruct Used by\n"); + list_for_each_entry(mod, kdb_modules, list) { + + kdb_printf("%-20s%8u 0x%p ", mod->name, + mod->core_size, (void *)mod); +#ifdef CONFIG_MODULE_UNLOAD + kdb_printf("%4d ", module_refcount(mod)); +#endif + if (mod->state == MODULE_STATE_GOING) + kdb_printf(" (Unloading)"); + else if (mod->state == MODULE_STATE_COMING) + kdb_printf(" (Loading)"); + else + kdb_printf(" (Live)"); + +#ifdef CONFIG_MODULE_UNLOAD + { + struct module_use *use; + kdb_printf(" [ "); + list_for_each_entry(use, &mod->modules_which_use_me, list) + kdb_printf("%s ", use->module_which_uses->name); + kdb_printf("]\n"); + } +#endif + } + + return 0; +} + +#endif /* CONFIG_MODULES */ + +/* + * kdb_env + * + * This function implements the 'env' command. Display the current + * environment variables. + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +static int +kdb_env(int argc, const char **argv) +{ + int i; + + for(i=0; i<__nenv; i++) { + if (__env[i]) { + kdb_printf("%s\n", __env[i]); + } + } + + if (KDB_DEBUG(MASK)) + kdb_printf("KDBFLAGS=0x%x\n", kdb_flags); + + return 0; +} + +/* + * kdb_dmesg + * + * This function implements the 'dmesg' command to display the contents + * of the syslog buffer. + * + * dmesg [lines] [adjust] + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * None. + */ + +static int +kdb_dmesg(int argc, const char **argv) +{ + char *syslog_data[4], *start, *end, c = '\0', *p; + int diag, logging, logsize, lines = 0, adjust = 0, n; + + if (argc > 2) + return KDB_ARGCOUNT; + if (argc) { + char *cp; + lines = simple_strtol(argv[1], &cp, 0); + if (*cp) + lines = 0; + if (argc > 1) { + adjust = simple_strtoul(argv[2], &cp, 0); + if (*cp || adjust < 0) + adjust = 0; + } + } + + /* disable LOGGING if set */ + diag = kdbgetintenv("LOGGING", &logging); + if (!diag && logging) { + const char *setargs[] = { "set", "LOGGING", "0" }; + kdb_set(2, setargs); + } + + /* syslog_data[0,1] physical start, end+1. syslog_data[2,3] logical start, end+1. */ + debugger_syslog_data(syslog_data); + if (syslog_data[2] == syslog_data[3]) + return 0; + logsize = syslog_data[1] - syslog_data[0]; + start = syslog_data[2]; + end = syslog_data[3]; +#define KDB_WRAP(p) (((p - syslog_data[0]) % logsize) + syslog_data[0]) + for (n = 0, p = start; p < end; ++p) { + if ((c = *KDB_WRAP(p)) == '\n') + ++n; + } + if (c != '\n') + ++n; + if (lines < 0) { + if (adjust >= n) + kdb_printf("buffer only contains %d lines, nothing printed\n", n); + else if (adjust - lines >= n) + kdb_printf("buffer only contains %d lines, last %d lines printed\n", + n, n - adjust); + if (adjust) { + for (; start < end && adjust; ++start) { + if (*KDB_WRAP(start) == '\n') + --adjust; + } + if (start < end) + ++start; + } + for (p = start; p < end && lines; ++p) { + if (*KDB_WRAP(p) == '\n') + ++lines; + } + end = p; + } else if (lines > 0) { + int skip = n - (adjust + lines); + if (adjust >= n) { + kdb_printf("buffer only contains %d lines, nothing printed\n", n); + skip = n; + } else if (skip < 0) { + lines += skip; + skip = 0; + kdb_printf("buffer only contains %d lines, first %d lines printed\n", + n, lines); + } + for (; start < end && skip; ++start) { + if (*KDB_WRAP(start) == '\n') + --skip; + } + for (p = start; p < end && lines; ++p) { + if (*KDB_WRAP(p) == '\n') + --lines; + } + end = p; + } + /* Do a line at a time (max 200 chars) to reduce protocol overhead */ + c = '\n'; + while (start != end) { + char buf[201]; + p = buf; + while (start < end && (c = *KDB_WRAP(start)) && (p - buf) < sizeof(buf)-1) { + ++start; + *p++ = c; + if (c == '\n') + break; + } + *p = '\0'; + kdb_printf("%s", buf); + } + if (c != '\n') + kdb_printf("\n"); + + return 0; +} + +/* + * kdb_cpu + * + * This function implements the 'cpu' command. + * + * cpu [] + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * KDB_CMD_CPU for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * All cpu's should be spinning in kdb(). However just in case + * a cpu did not take the smp_kdb_stop NMI, check that a cpu + * entered kdb() before passing control to it. + */ + +static void +kdb_cpu_status(void) +{ + int i, start_cpu, first_print = 1; + char state, prev_state = '?'; + + kdb_printf("Currently on cpu %d\n", smp_processor_id()); + kdb_printf("Available cpus: "); + for (start_cpu = -1, i = 0; i < NR_CPUS; i++) { + if (!cpu_online(i)) + state = 'F'; /* cpu is offline */ + else { + struct kdb_running_process *krp = kdb_running_process+i; + if (KDB_STATE_CPU(KDB, i)) { + state = ' '; /* cpu is responding to kdb */ + if (kdb_task_state_char(krp->p) == 'I') + state = 'I'; /* running the idle task */ + } else if (krp->seqno && krp->p && krp->seqno >= kdb_seqno - 1) + state = '+'; /* some kdb data, but not responding */ + else + state = '*'; /* no kdb data */ + } + if (state != prev_state) { + if (prev_state != '?') { + if (!first_print) + kdb_printf(", "); + first_print = 0; + kdb_printf("%d", start_cpu); + if (start_cpu < i-1) + kdb_printf("-%d", i-1); + if (prev_state != ' ') + kdb_printf("(%c)", prev_state); + } + prev_state = state; + start_cpu = i; + } + } + /* print the trailing cpus, ignoring them if they are all offline */ + if (prev_state != 'F') { + if (!first_print) + kdb_printf(", "); + kdb_printf("%d", start_cpu); + if (start_cpu < i-1) + kdb_printf("-%d", i-1); + if (prev_state != ' ') + kdb_printf("(%c)", prev_state); + } + kdb_printf("\n"); +} + +static int +kdb_cpu(int argc, const char **argv) +{ + unsigned long cpunum; + int diag, i; + + /* ask the other cpus if they are still active */ + for (i=0; i NR_CPUS) + || !cpu_online(cpunum) + || !KDB_STATE_CPU(KDB, cpunum)) + return KDB_BADCPUNUM; + + kdb_new_cpu = cpunum; + + /* + * Switch to other cpu + */ + return KDB_CMD_CPU; +} + +/* The user may not realize that ps/bta with no parameters does not print idle + * or sleeping system daemon processes, so tell them how many were suppressed. + */ +void +kdb_ps_suppressed(void) +{ + int idle = 0, daemon = 0; + unsigned long mask_I = kdb_task_state_string("I"), + mask_M = kdb_task_state_string("M"); + unsigned long cpu; + const struct task_struct *p, *g; + for (cpu = 0; cpu < NR_CPUS; ++cpu) { + if (!cpu_online(cpu)) + continue; + p = kdb_curr_task(cpu); + if (kdb_task_state(p, mask_I)) + ++idle; + } + kdb_do_each_thread(g, p) { + if (kdb_task_state(p, mask_M)) + ++daemon; + } kdb_while_each_thread(g, p); + if (idle || daemon) { + if (idle) + kdb_printf("%d idle process%s (state I)%s\n", + idle, idle == 1 ? "" : "es", + daemon ? " and " : ""); + if (daemon) + kdb_printf("%d sleeping system daemon (state M) process%s", + daemon, daemon == 1 ? "" : "es"); + kdb_printf(" suppressed,\nuse 'ps A' to see all.\n"); + } +} + +/* + * kdb_ps + * + * This function implements the 'ps' command which shows + * a list of the active processes. + * + * ps [DRSTCZEUIMA] All processes, optionally filtered by state + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +void +kdb_ps1(const struct task_struct *p) +{ + struct kdb_running_process *krp = kdb_running_process + kdb_process_cpu(p); + kdb_printf("0x%p %8d %8d %d %4d %c 0x%p %c%s\n", + (void *)p, p->pid, p->parent->pid, + kdb_task_has_cpu(p), kdb_process_cpu(p), + kdb_task_state_char(p), + (void *)(&p->thread), + p == kdb_curr_task(smp_processor_id()) ? '*': ' ', + p->comm); + if (kdb_task_has_cpu(p)) { + if (!krp->seqno || !krp->p) + kdb_printf(" Error: no saved data for this cpu\n"); + else { + if (krp->seqno < kdb_seqno - 1) + kdb_printf(" Warning: process state is stale\n"); + if (krp->p != p) + kdb_printf(" Error: does not match running process table (0x%p)\n", krp->p); + } + } +} + +static int +kdb_ps(int argc, const char **argv) +{ + struct task_struct *g, *p; + unsigned long mask, cpu; + + if (argc == 0) + kdb_ps_suppressed(); + kdb_printf("%-*s Pid Parent [*] cpu State %-*s Command\n", + (int)(2*sizeof(void *))+2, "Task Addr", + (int)(2*sizeof(void *))+2, "Thread"); + mask = kdb_task_state_string(argc ? argv[1] : NULL); + /* Run the active tasks first */ + for (cpu = 0; cpu < NR_CPUS; ++cpu) { + if (!cpu_online(cpu)) + continue; + p = kdb_curr_task(cpu); + if (kdb_task_state(p, mask)) + kdb_ps1(p); + } + kdb_printf("\n"); + /* Now the real tasks */ + kdb_do_each_thread(g, p) { + if (kdb_task_state(p, mask)) + kdb_ps1(p); + } kdb_while_each_thread(g, p); + + return 0; +} + +/* + * kdb_pid + * + * This function implements the 'pid' command which switches + * the currently active process. + * + * pid [ | R] + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + + +static int +kdb_pid(int argc, const char **argv) +{ + struct task_struct *p; + unsigned long val; + int diag; + + if (argc > 1) + return KDB_ARGCOUNT; + + if (argc) { + if (strcmp(argv[1], "R") == 0) { + p = KDB_RUNNING_PROCESS_ORIGINAL[kdb_initial_cpu].p; + } else { + diag = kdbgetularg(argv[1], &val); + if (diag) + return KDB_BADINT; + + p = find_task_by_pid_ns((pid_t)val, &init_pid_ns); + if (!p) { + kdb_printf("No task with pid=%d\n", (pid_t)val); + return 0; + } + } + + kdba_set_current_task(p); + } + + kdb_printf("KDB current process is %s(pid=%d)\n", kdb_current_task->comm, + kdb_current_task->pid); + + 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 + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +static int +kdb_ll(int argc, const char **argv) +{ + int diag; + kdb_machreg_t addr; + long offset = 0; + kdb_machreg_t va; + unsigned long linkoffset; + int nextarg; + const char *command; + + if (argc != 3) { + return KDB_ARGCOUNT; + } + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); + 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; + if (!(command = kdb_strdup(argv[3], GFP_KDB))) { + kdb_printf("%s: cannot duplicate command\n", __FUNCTION__); + return 0; + } + /* Recursive use of kdb_parse, do not use argv after this point */ + argv = NULL; + + while (va) { + char buf[80]; + + sprintf(buf, "%s " kdb_machreg_fmt "\n", command, va); + diag = kdb_parse(buf); + if (diag) + return diag; + + addr = va + linkoffset; + if (kdb_getword(&va, addr, sizeof(va))) + return 0; + } + kfree(command); + + return 0; +} + +/* + * kdb_help + * + * This function implements the 'help' and '?' commands. + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +static int +kdb_help(int argc, const char **argv) +{ + kdbtab_t *kt; + int i; + + kdb_printf("%-15.15s %-20.20s %s\n", "Command", "Usage", "Description"); + kdb_printf("----------------------------------------------------------\n"); + for(i=0, kt=kdb_commands; icmd_name) + kdb_printf("%-15.15s %-20.20s %s\n", kt->cmd_name, + kt->cmd_usage, kt->cmd_help); + } + return 0; +} + +extern int kdb_wake_up_process(struct task_struct * p); + +/* + * kdb_kill + * + * This function implements the 'kill' commands. + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +static int +kdb_kill(int argc, const char **argv) +{ + long sig, pid; + char *endp; + struct task_struct *p; + struct siginfo info; + + if (argc!=2) + return KDB_ARGCOUNT; + + sig = simple_strtol(argv[1], &endp, 0); + if (*endp) + return KDB_BADINT; + if (sig >= 0 ) { + kdb_printf("Invalid signal parameter.<-signal>\n"); + return 0; + } + sig=-sig; + + pid = simple_strtol(argv[2], &endp, 0); + if (*endp) + return KDB_BADINT; + if (pid <=0 ) { + kdb_printf("Process ID must be large than 0.\n"); + return 0; + } + + /* Find the process. */ + if (!(p = find_task_by_pid_ns(pid, &init_pid_ns))) { + kdb_printf("The specified process isn't found.\n"); + return 0; + } + p = p->group_leader; + info.si_signo = sig; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = pid; /* use same capabilities as process being signalled */ + info.si_uid = 0; /* kdb has root authority */ + kdb_send_sig_info(p, &info, kdb_seqno); + return 0; +} + +struct kdb_tm { + int tm_sec; /* seconds */ + int tm_min; /* minutes */ + int tm_hour; /* hours */ + int tm_mday; /* day of the month */ + int tm_mon; /* month */ + int tm_year; /* year */ +}; + +static void +kdb_gmtime(struct timespec *tv, struct kdb_tm *tm) +{ + /* This will work from 1970-2099, 2100 is not a leap year */ + static int mon_day[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + memset(tm, 0, sizeof(*tm)); + tm->tm_sec = tv->tv_sec % (24 * 60 * 60); + tm->tm_mday = tv->tv_sec / (24 * 60 * 60) + (2 * 365 + 1); /* shift base from 1970 to 1968 */ + tm->tm_min = tm->tm_sec / 60 % 60; + tm->tm_hour = tm->tm_sec / 60 / 60; + tm->tm_sec = tm->tm_sec % 60; + tm->tm_year = 68 + 4*(tm->tm_mday / (4*365+1)); + tm->tm_mday %= (4*365+1); + mon_day[1] = 29; + while (tm->tm_mday >= mon_day[tm->tm_mon]) { + tm->tm_mday -= mon_day[tm->tm_mon]; + if (++tm->tm_mon == 12) { + tm->tm_mon = 0; + ++tm->tm_year; + mon_day[1] = 28; + } + } + ++tm->tm_mday; +} + +/* + * Most of this code has been lifted from kernel/timer.c::sys_sysinfo(). + * I cannot call that code directly from kdb, it has an unconditional + * cli()/sti() and calls routines that take locks which can stop the debugger. + */ + +static void +kdb_sysinfo(struct sysinfo *val) +{ + struct timespec uptime; + do_posix_clock_monotonic_gettime(&uptime); + memset(val, 0, sizeof(*val)); + val->uptime = uptime.tv_sec; + val->loads[0] = avenrun[0]; + val->loads[1] = avenrun[1]; + val->loads[2] = avenrun[2]; + val->procs = nr_threads-1; + si_meminfo(val); + kdb_si_swapinfo(val); + + return; +} + +/* + * kdb_summary + * + * This function implements the 'summary' command. + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +static int +kdb_summary(int argc, const char **argv) +{ + extern struct timespec xtime; + extern struct timezone sys_tz; + struct kdb_tm tm; + struct sysinfo val; + + if (argc) + return KDB_ARGCOUNT; + + kdb_printf("sysname %s\n", init_uts_ns.name.sysname); + kdb_printf("release %s\n", init_uts_ns.name.release); + kdb_printf("version %s\n", init_uts_ns.name.version); + kdb_printf("machine %s\n", init_uts_ns.name.machine); + kdb_printf("nodename %s\n", init_uts_ns.name.nodename); + kdb_printf("domainname %s\n", init_uts_ns.name.domainname); + kdb_printf("ccversion %s\n", __stringify(CCVERSION)); + + kdb_gmtime(&xtime, &tm); + kdb_printf("date %04d-%02d-%02d %02d:%02d:%02d tz_minuteswest %d\n", + 1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, + sys_tz.tz_minuteswest); + + kdb_sysinfo(&val); + kdb_printf("uptime "); + if (val.uptime > (24*60*60)) { + int days = val.uptime / (24*60*60); + val.uptime %= (24*60*60); + kdb_printf("%d day%s ", days, days == 1 ? "" : "s"); + } + kdb_printf("%02ld:%02ld\n", val.uptime/(60*60), (val.uptime/60)%60); + + /* lifted from fs/proc/proc_misc.c::loadavg_read_proc() */ + +#define LOAD_INT(x) ((x) >> FSHIFT) +#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) + kdb_printf("load avg %ld.%02ld %ld.%02ld %ld.%02ld\n", + LOAD_INT(val.loads[0]), LOAD_FRAC(val.loads[0]), + LOAD_INT(val.loads[1]), LOAD_FRAC(val.loads[1]), + LOAD_INT(val.loads[2]), LOAD_FRAC(val.loads[2])); + kdb_printf("\n"); +#undef LOAD_INT +#undef LOAD_FRAC + + kdb_meminfo_proc_show(); /* in fs/proc/meminfo.c */ + + return 0; +} + +/* + * kdb_per_cpu + * + * This function implements the 'per_cpu' command. + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ + +static int +kdb_per_cpu(int argc, const char **argv) +{ + char buf[256], fmtstr[64]; + kdb_symtab_t symtab; + cpumask_t suppress; + int cpu, diag; + unsigned long addr, val, bytesperword = 0, whichcpu = ~0UL; + + if (argc < 1 || argc > 3) + return KDB_ARGCOUNT; + + cpus_clear(suppress); + snprintf(buf, sizeof(buf), "per_cpu__%s", argv[1]); + if (!kdbgetsymval(buf, &symtab)) { + kdb_printf("%s is not a per_cpu variable\n", argv[1]); + return KDB_BADADDR; + } + if (argc >=2 && (diag = kdbgetularg(argv[2], &bytesperword))) + return diag; + if (!bytesperword) + bytesperword = KDB_WORD_SIZE; + else if (bytesperword > KDB_WORD_SIZE) + return KDB_BADWIDTH; + sprintf(fmtstr, "%%0%dlx ", (int)(2*bytesperword)); + if (argc >= 3) { + if ((diag = kdbgetularg(argv[3], &whichcpu))) + return diag; + if (!cpu_online(whichcpu)) { + kdb_printf("cpu %ld is not online\n", whichcpu); + return KDB_BADCPUNUM; + } + } + + /* Most architectures use __per_cpu_offset[cpu], some use + * __per_cpu_offset(cpu), smp has no __per_cpu_offset. + */ +#ifdef __per_cpu_offset +#define KDB_PCU(cpu) __per_cpu_offset(cpu) +#else +#ifdef CONFIG_SMP +#define KDB_PCU(cpu) __per_cpu_offset[cpu] +#else +#define KDB_PCU(cpu) 0 +#endif +#endif + + for_each_online_cpu(cpu) { + if (whichcpu != ~0UL && whichcpu != cpu) + continue; + addr = symtab.sym_start + KDB_PCU(cpu); + if ((diag = kdb_getword(&val, addr, bytesperword))) { + kdb_printf("%5d " kdb_bfd_vma_fmt0 " - unable to read, diag=%d\n", + cpu, addr, diag); + continue; + } +#ifdef CONFIG_SMP + if (!val) { + cpu_set(cpu, suppress); + continue; + } +#endif /* CONFIG_SMP */ + kdb_printf("%5d ", cpu); + kdb_md_line(fmtstr, addr, + bytesperword == KDB_WORD_SIZE, + 1, bytesperword, 1, 1, 0); + } + if (cpus_weight(suppress) == 0) + return 0; + kdb_printf("Zero suppressed cpu(s):"); + for_each_cpu_mask(cpu, suppress) { + kdb_printf(" %d", cpu); + if (cpu == NR_CPUS-1 || next_cpu(cpu, suppress) != cpu + 1) + continue; + while (cpu < NR_CPUS && next_cpu(cpu, suppress) == cpu + 1) + ++cpu; + kdb_printf("-%d", cpu); + } + kdb_printf("\n"); + +#undef KDB_PCU + + return 0; +} + +/* + * display help for the use of cmd | grep pattern + */ +static int +kdb_grep_help(int argc, const char **argv) +{ + kdb_printf ("Usage of cmd args | grep pattern:\n"); + kdb_printf (" Any command's output may be filtered through an "); + kdb_printf ("emulated 'pipe'.\n"); + kdb_printf (" 'grep' is just a key word.\n"); + kdb_printf + (" The pattern may include a very limited set of metacharacters:\n"); + kdb_printf (" pattern or ^pattern or pattern$ or ^pattern$\n"); + kdb_printf + (" And if there are spaces in the pattern, you may quote it:\n"); + kdb_printf + (" \"pat tern\" or \"^pat tern\" or \"pat tern$\" or \"^pat tern$\"\n"); + return 0; +} + +/* + * kdb_register_repeat + * + * 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 + * repeat Does the command auto repeat on enter? + * Outputs: + * None. + * Returns: + * zero for success, one if a duplicate command. + * Locking: + * none. + * Remarks: + * + */ + +#define kdb_command_extend 50 /* arbitrary */ +int +kdb_register_repeat(char *cmd, + kdb_func_t func, + char *usage, + char *help, + short minlen, + kdb_repeat_t repeat) +{ + 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, func %p help %s\n", cmd, func, help); + return 1; + } + } + + /* + * Insert command into first available location in table + */ + for (i=0, kp=kdb_commands; icmd_name == NULL) { + break; + } + } + + if (i >= kdb_max_commands) { + kdbtab_t *new = kmalloc((kdb_max_commands + kdb_command_extend) * sizeof(*new), GFP_KDB); + if (!new) { + kdb_printf("Could not allocate new kdb_command table\n"); + return 1; + } + if (kdb_commands) { + memcpy(new, kdb_commands, kdb_max_commands * sizeof(*new)); + kfree(kdb_commands); + } + memset(new + kdb_max_commands, 0, kdb_command_extend * sizeof(*new)); + kdb_commands = new; + kp = kdb_commands + kdb_max_commands; + kdb_max_commands += kdb_command_extend; + } + + kp->cmd_name = cmd; + kp->cmd_func = func; + kp->cmd_usage = usage; + kp->cmd_help = help; + kp->cmd_flags = 0; + kp->cmd_minlen = minlen; + kp->cmd_repeat = repeat; + + return 0; +} + +/* + * kdb_register + * + * Compatibility register function for commands that do not need to + * specify a repeat state. Equivalent to kdb_register_repeat with + * KDB_REPEAT_NONE. + * + * Inputs: + * cmd Command name + * func Function to execute the command + * usage A simple usage string showing arguments + * help A simple help string describing command + * Outputs: + * None. + * Returns: + * zero for success, one if a duplicate command. + * Locking: + * none. + * Remarks: + * + */ + +int +kdb_register(char *cmd, + kdb_func_t func, + char *usage, + char *help, + short minlen) +{ + return kdb_register_repeat(cmd, func, usage, help, minlen, KDB_REPEAT_NONE); +} + +/* + * 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_repeat. + * + * Inputs: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + */ + +static void __init +kdb_inittab(void) +{ + int i; + kdbtab_t *kp; + + for(i=0, kp=kdb_commands; i < kdb_max_commands; i++,kp++) { + kp->cmd_name = NULL; + } + + kdb_register_repeat("md", kdb_md, "", "Display Memory Contents, also mdWcN, e.g. md8c1", 1, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("mdr", kdb_md, " ", "Display Raw Memory", 0, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("mdp", kdb_md, " ", "Display Physical Memory", 0, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("mds", kdb_md, "", "Display Memory Symbolically", 0, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("mm", kdb_mm, " ", "Modify Memory Contents", 0, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("id", kdb_id, "", "Display Instructions", 1, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("go", kdb_go, "[]", "Continue Execution", 1, KDB_REPEAT_NONE); + kdb_register_repeat("rd", kdb_rd, "", "Display Registers", 1, KDB_REPEAT_NONE); + kdb_register_repeat("rm", kdb_rm, " ", "Modify Registers", 0, KDB_REPEAT_NONE); + kdb_register_repeat("ef", kdb_ef, "", "Display exception frame", 0, KDB_REPEAT_NONE); + kdb_register_repeat("bt", kdb_bt, "[]", "Stack traceback", 1, KDB_REPEAT_NONE); + kdb_register_repeat("btp", kdb_bt, "", "Display stack for process ", 0, KDB_REPEAT_NONE); + kdb_register_repeat("bta", kdb_bt, "[DRSTCZEUIMA]", "Display stack all processes", 0, KDB_REPEAT_NONE); + kdb_register_repeat("btc", kdb_bt, "", "Backtrace current process on each cpu", 0, KDB_REPEAT_NONE); + kdb_register_repeat("btt", kdb_bt, "", "Backtrace process given its struct task address", 0, KDB_REPEAT_NONE); + kdb_register_repeat("ll", kdb_ll, " ", "Execute cmd for each element in linked list", 0, KDB_REPEAT_NONE); + kdb_register_repeat("env", kdb_env, "", "Show environment variables", 0, KDB_REPEAT_NONE); + kdb_register_repeat("set", kdb_set, "", "Set environment variables", 0, KDB_REPEAT_NONE); + kdb_register_repeat("help", kdb_help, "", "Display Help Message", 1, KDB_REPEAT_NONE); + kdb_register_repeat("?", kdb_help, "", "Display Help Message", 0, KDB_REPEAT_NONE); + kdb_register_repeat("cpu", kdb_cpu, "","Switch to new cpu", 0, KDB_REPEAT_NONE); + kdb_register_repeat("ps", kdb_ps, "[|A]", "Display active task list", 0, KDB_REPEAT_NONE); + kdb_register_repeat("pid", kdb_pid, "", "Switch to another task", 0, KDB_REPEAT_NONE); + kdb_register_repeat("reboot", kdb_reboot, "", "Reboot the machine immediately", 0, KDB_REPEAT_NONE); +#if defined(CONFIG_KDB_KDUMP) + kdb_register_repeat("kdump", kdb_kdump, "", "Calls kdump mode", 0, KDB_REPEAT_NONE); +#endif +#if defined(CONFIG_MODULES) + kdb_register_repeat("lsmod", kdb_lsmod, "", "List loaded kernel modules", 0, KDB_REPEAT_NONE); +#endif +#if defined(CONFIG_MAGIC_SYSRQ) + kdb_register_repeat("sr", kdb_sr, "", "Magic SysRq key", 0, KDB_REPEAT_NONE); +#endif + kdb_register_repeat("dmesg", kdb_dmesg, "[lines]", "Display syslog buffer", 0, KDB_REPEAT_NONE); + kdb_register_repeat("defcmd", kdb_defcmd, "name \"usage\" \"help\"", "Define a set of commands, down to endefcmd", 0, KDB_REPEAT_NONE); + kdb_register_repeat("kill", kdb_kill, "<-signal> ", "Send a signal to a process", 0, KDB_REPEAT_NONE); + kdb_register_repeat("summary", kdb_summary, "", "Summarize the system", 4, KDB_REPEAT_NONE); + kdb_register_repeat("per_cpu", kdb_per_cpu, "", "Display per_cpu variables", 3, KDB_REPEAT_NONE); + kdb_register_repeat("grephelp", kdb_grep_help, "", + "Display help on | grep", 0, KDB_REPEAT_NONE); + kdb_register_repeat("print", kdb_debuginfo_print, "", + "Type casting, as in lcrash", 0, KDB_REPEAT_NONE); + kdb_register_repeat("px", kdb_debuginfo_print, "", + "Print in hex (type casting) (see 'pxhelp')", 0, KDB_REPEAT_NONE); + kdb_register_repeat("pxhelp", kdb_pxhelp, "", + "Display help for the px command", 0, KDB_REPEAT_NONE); + kdb_register_repeat("pd", kdb_debuginfo_print, "", + "Print in decimal (type casting)", 0, KDB_REPEAT_NONE); + kdb_register_repeat("whatis", kdb_debuginfo_print,"", + "Display the type, or the address for a symbol", 0, KDB_REPEAT_NONE); + kdb_register_repeat("sizeof", kdb_debuginfo_print, "", + "Display the size of a structure, typedef, etc.", 0, KDB_REPEAT_NONE); + kdb_register_repeat("walk", kdb_walk, "", + "Walk a linked list (see 'walkhelp')", 0, KDB_REPEAT_NONE); + kdb_register_repeat("walkhelp", kdb_walkhelp, "", + "Display help for the walk command", 0, KDB_REPEAT_NONE); +} + +/* + * The user has written to our "file" + * file: the /proc file + * buffer: user address of the data he is writing + * count: number of bytes in the user's buffer + */ +static int +kdb_write_proc_filename(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + int ret_count; + + /* our buffer is kdb_debug_info_filename[256] */ + if (count > 256) { + return 0; + } + if (copy_from_user(kdb_debug_info_filename, buffer, count)) { + return 0; + } + ret_count = count; /* actual count */ + /* remove any newline from the end of the file name */ + if (kdb_debug_info_filename[count-1] == '\n') count--; + kdb_debug_info_filename[count] = '\0'; + + return ret_count; +} + +/* + * The user is reading from our "file" + * page: the beginning of the user's buffer + * start: pointer to the user's pointer (tells him where we put the data) + * off: offset into the resource to be read + * count: length of the read + */ +static int +kdb_read_proc_filename(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + /* give him kdb_debug_info_filename[]; */ + return snprintf(page, count, "%s\n", kdb_debug_info_filename); +} + +/* + * kdb_proc_filename + * + * create /proc/kdb/debug_info_name + */ +static void +kdb_proc_filename(void) +{ + struct proc_dir_entry *kdb_dir_entry, *kdb_file_entry; + + /* create /proc/kdb */ + kdb_dir_entry = proc_mkdir("kdb", NULL); + if (!kdb_dir_entry) { + printk ("kdb could not create /proc/kdb\n"); + return; + } + + /* read/write by owner (root) only */ + kdb_file_entry = create_proc_entry("debug_info_name", + S_IRUSR | S_IWUSR, kdb_dir_entry); + if (!kdb_file_entry) { + printk ("kdb could not create /proc/kdb/kdb_dir_entry\n"); + return; + } + kdb_file_entry->nlink = 1; + kdb_file_entry->data = (void *)NULL; + kdb_file_entry->read_proc = kdb_read_proc_filename; + kdb_file_entry->write_proc = kdb_write_proc_filename; + return; +} + +/* + * kdb_cmd_init + * + * This function is called by the kdb_init function to execute any + * commands defined in kdb_cmds. + * + * Inputs: + * Commands in *kdb_cmds[]; + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + */ + +static void __init +kdb_cmd_init(void) +{ + int i, diag; + for (i = 0; kdb_cmds[i]; ++i) { + if (!defcmd_in_progress) + if (console_loglevel >= 6 /* KERN_INFO */) + kdb_printf("kdb_cmd[%d]: %s", i, kdb_cmds[i]); + diag = kdb_parse(kdb_cmds[i]); + if (diag) + kdb_printf("kdb command %s failed, kdb diag %d\n", + kdb_cmds[i], diag); + } + if (defcmd_in_progress) { + kdb_printf("Incomplete 'defcmd' set, forcing endefcmd\n"); + kdb_parse("endefcmd"); + } +} + +/* + * kdb_panic + * + * Invoked via the panic_notifier_list. + * + * Inputs: + * None. + * Outputs: + * None. + * Returns: + * Zero. + * Locking: + * None. + * Remarks: + * When this function is called from panic(), the other cpus have already + * been stopped. + * + */ + +static int +kdb_panic(struct notifier_block *self, unsigned long command, void *ptr) +{ + KDB_FLAG_SET(CATASTROPHIC); /* kernel state is dubious now */ + KDB_ENTER(); + return 0; +} + +static struct notifier_block kdb_block = { kdb_panic, NULL, 0 }; + +#ifdef CONFIG_SYSCTL +static int proc_do_kdb(ctl_table *table, int write, void __user *buffer, + size_t *lenp, loff_t *ppos) +{ + if (KDB_FLAG(NO_CONSOLE) && write) { + printk(KERN_ERR "kdb has no working console and has switched itself off\n"); + return -EINVAL; + } + return proc_dointvec(table, write, buffer, lenp, ppos); +} + +static ctl_table kdb_kern_table[] = { + { + .procname = "kdb", + .data = &kdb_on, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_do_kdb, + }, + {} +}; + +static ctl_table kdb_root_table[] = { + { + .procname = "kernel", + .mode = 0555, + .child = kdb_kern_table, + }, + {} +}; +#endif /* CONFIG_SYSCTL */ + +static int +kdb_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) +{ + if (action == CPU_ONLINE) { + int cpu =(unsigned long)hcpu; + cpumask_t save_cpus_allowed = current->cpus_allowed; + set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + kdb(KDB_REASON_CPU_UP, 0, NULL); /* do kdb setup on this cpu */ + set_cpus_allowed_ptr(current, &save_cpus_allowed); + } + return NOTIFY_OK; +} + +static struct notifier_block kdb_cpu_nfb = { + .notifier_call = kdb_cpu_callback +}; + +/* + * kdb_init + * + * Initialize the kernel debugger environment. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +void __init +kdb_init(void) +{ + kdb_initial_cpu = smp_processor_id(); + /* + * This must be called before any calls to kdb_printf. + */ + kdb_io_init(); + + kdb_inittab(); /* Initialize Command Table */ + kdb_initbptab(); /* Initialize Breakpoint Table */ + kdb_id_init(); /* Initialize Disassembler */ + kdba_init(); /* Architecture Dependent Initialization */ + + /* + * Use printk() to get message in log_buf[]; + */ + printk("kdb version %d.%d%s by Keith Owens, Scott Lurndal. "\ + "Copyright SGI, All Rights Reserved\n", + KDB_MAJOR_VERSION, KDB_MINOR_VERSION, KDB_TEST_VERSION); + + kdb_cmd_init(); /* Preset commands from kdb_cmds */ + kdb_initial_cpu = -1; /* Avoid recursion problems */ + kdb(KDB_REASON_CPU_UP, 0, NULL); /* do kdb setup on boot cpu */ + kdb_initial_cpu = smp_processor_id(); + atomic_notifier_chain_register(&panic_notifier_list, &kdb_block); + register_cpu_notifier(&kdb_cpu_nfb); + +#ifdef kdba_setjmp + kdbjmpbuf = vmalloc(NR_CPUS * sizeof(*kdbjmpbuf)); + if (!kdbjmpbuf) + printk(KERN_ERR "Cannot allocate kdbjmpbuf, no kdb recovery will be possible\n"); +#endif /* kdba_setjmp */ + + kdb_initial_cpu = -1; + kdb_wait_for_cpus_secs = 2*num_online_cpus(); + kdb_wait_for_cpus_secs = max(kdb_wait_for_cpus_secs, 10); +} + +#ifdef CONFIG_SYSCTL +static int __init +kdb_late_init(void) +{ + register_sysctl_table(kdb_root_table); + /* seems that we cannot allocate with kmalloc until now */ + kdb_proc_filename(); + return 0; +} + +__initcall(kdb_late_init); +#endif + +EXPORT_SYMBOL(kdb_register); +EXPORT_SYMBOL(kdb_register_repeat); +EXPORT_SYMBOL(kdb_unregister); +EXPORT_SYMBOL(kdb_getarea_size); +EXPORT_SYMBOL(kdb_putarea_size); +EXPORT_SYMBOL(kdb_getuserarea_size); +EXPORT_SYMBOL(kdb_putuserarea_size); +EXPORT_SYMBOL(kdbgetularg); +EXPORT_SYMBOL(kdbgetenv); +EXPORT_SYMBOL(kdbgetintenv); +EXPORT_SYMBOL(kdbgetaddrarg); +EXPORT_SYMBOL(kdb); +EXPORT_SYMBOL(kdb_on); +EXPORT_SYMBOL(kdb_seqno); +EXPORT_SYMBOL(kdb_initial_cpu); +EXPORT_SYMBOL(kdbnearsym); +EXPORT_SYMBOL(kdb_printf); +EXPORT_SYMBOL(kdb_symbol_print); +EXPORT_SYMBOL(kdb_running_process); --- /dev/null +++ b/kdb/kdbsupport.c @@ -0,0 +1,1155 @@ +/* + * Kernel Debugger Architecture Independent Support Functions + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. + * 03/02/13 added new 2.5 kallsyms + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* + * Symbol table functions. + */ + +/* + * kdbgetsymval + * + * Return the address of the given symbol. + * + * Parameters: + * symname Character string containing symbol name + * symtab Structure to receive results + * Outputs: + * Returns: + * 0 Symbol not found, symtab zero filled + * 1 Symbol mapped to module/symbol/section, data in symtab + * Locking: + * None. + * Remarks: + */ + +int +kdbgetsymval(const char *symname, kdb_symtab_t *symtab) +{ + if (KDB_DEBUG(AR)) + kdb_printf("kdbgetsymval: symname=%s, symtab=%p\n", symname, symtab); + memset(symtab, 0, sizeof(*symtab)); + + if ((symtab->sym_start = kallsyms_lookup_name(symname))) { + if (KDB_DEBUG(AR)) + kdb_printf("kdbgetsymval: returns 1, symtab->sym_start=0x%lx\n", symtab->sym_start); + return 1; + } + if (KDB_DEBUG(AR)) + kdb_printf("kdbgetsymval: returns 0\n"); + return 0; +} +EXPORT_SYMBOL(kdbgetsymval); + +/* + * kdbnearsym + * + * Return the name of the symbol with the nearest address + * less than 'addr'. + * + * Parameters: + * addr Address to check for symbol near + * symtab Structure to receive results + * Outputs: + * Returns: + * 0 No sections contain this address, symtab zero filled + * 1 Address mapped to module/symbol/section, data in symtab + * Locking: + * None. + * Remarks: + * 2.6 kallsyms has a "feature" where it unpacks the name into a string. + * If that string is reused before the caller expects it then the caller + * sees its string change without warning. To avoid cluttering up the + * main kdb code with lots of kdb_strdup, tests and kfree calls, kdbnearsym + * maintains an LRU list of the last few unique strings. The list is sized + * large enough to hold active strings, no kdb caller of kdbnearsym makes + * more than ~20 later calls before using a saved value. + */ + +static char *kdb_name_table[100]; /* arbitrary size */ + +int +kdbnearsym(unsigned long addr, kdb_symtab_t *symtab) +{ + int ret = 0; + unsigned long symbolsize; + unsigned long offset; +#define knt1_size 128 /* must be >= kallsyms table size */ + char *knt1 = NULL; + + if (KDB_DEBUG(AR)) + kdb_printf("kdbnearsym: addr=0x%lx, symtab=%p\n", addr, symtab); + memset(symtab, 0, sizeof(*symtab)); + + if (addr < 4096) + goto out; + knt1 = debug_kmalloc(knt1_size, GFP_ATOMIC); + if (!knt1) { + kdb_printf("kdbnearsym: addr=0x%lx cannot kmalloc knt1\n", addr); + goto out; + } + symtab->sym_name = kallsyms_lookup(addr, &symbolsize , &offset, (char **)(&symtab->mod_name), knt1); + if (offset > 8*1024*1024) { + symtab->sym_name = NULL; + addr = offset = symbolsize = 0; + } + symtab->sym_start = addr - offset; + symtab->sym_end = symtab->sym_start + symbolsize; + ret = symtab->sym_name != NULL && *(symtab->sym_name) != '\0'; + + if (ret) { + int i; + /* Another 2.6 kallsyms "feature". Sometimes the sym_name is + * set but the buffer passed into kallsyms_lookup is not used, + * so it contains garbage. The caller has to work out which + * buffer needs to be saved. + * + * What was Rusty smoking when he wrote that code? + */ + if (symtab->sym_name != knt1) { + strncpy(knt1, symtab->sym_name, knt1_size); + knt1[knt1_size-1] = '\0'; + } + for (i = 0; i < ARRAY_SIZE(kdb_name_table); ++i) { + if (kdb_name_table[i] && strcmp(kdb_name_table[i], knt1) == 0) + break; + } + if (i >= ARRAY_SIZE(kdb_name_table)) { + debug_kfree(kdb_name_table[0]); + memcpy(kdb_name_table, kdb_name_table+1, + sizeof(kdb_name_table[0])*(ARRAY_SIZE(kdb_name_table)-1)); + } else { + debug_kfree(knt1); + knt1 = kdb_name_table[i]; + memcpy(kdb_name_table+i, kdb_name_table+i+1, + sizeof(kdb_name_table[0])*(ARRAY_SIZE(kdb_name_table)-i-1)); + } + i = ARRAY_SIZE(kdb_name_table) - 1; + kdb_name_table[i] = knt1; + symtab->sym_name = kdb_name_table[i]; + knt1 = NULL; + } + + if (symtab->mod_name == NULL) + symtab->mod_name = "kernel"; + if (KDB_DEBUG(AR)) + kdb_printf("kdbnearsym: returns %d symtab->sym_start=0x%lx, symtab->mod_name=%p, symtab->sym_name=%p (%s)\n", ret, symtab->sym_start, symtab->mod_name, symtab->sym_name, symtab->sym_name); + +out: + debug_kfree(knt1); + return ret; +} + +void +kdbnearsym_cleanup(void) +{ + int i; + for (i = 0; i < ARRAY_SIZE(kdb_name_table); ++i) { + if (kdb_name_table[i]) { + debug_kfree(kdb_name_table[i]); + kdb_name_table[i] = NULL; + } + } +} + +/* + * kallsyms_symbol_complete + * + * Parameters: + * prefix_name prefix of a symbol name to lookup + * max_len maximum length that can be returned + * Returns: + * Number of symbols which match the given prefix. + * Notes: + * prefix_name is changed to contain the longest unique prefix that + * starts with this prefix (tab completion). + */ + +static char ks_namebuf[KSYM_NAME_LEN+1], ks_namebuf_prev[KSYM_NAME_LEN+1]; + +int kallsyms_symbol_complete(char *prefix_name, int max_len) +{ + loff_t pos = 0; + int prefix_len = strlen(prefix_name), prev_len = 0; + int i, number = 0; + const char *name; + + while ((name = kdb_walk_kallsyms(&pos))) { + if (strncmp(name, prefix_name, prefix_len) == 0) { + strcpy(ks_namebuf, name); + /* Work out the longest name that matches the prefix */ + if (++number == 1) { + prev_len = min_t(int, max_len-1, strlen(ks_namebuf)); + memcpy(ks_namebuf_prev, ks_namebuf, prev_len); + ks_namebuf_prev[prev_len] = '\0'; + } else for (i = 0; i < prev_len; ++i) { + if (ks_namebuf[i] != ks_namebuf_prev[i]) { + prev_len = i; + ks_namebuf_prev[i] = '\0'; + break; + } + } + } + } + if (prev_len > prefix_len) + memcpy(prefix_name, ks_namebuf_prev, prev_len+1); + return number; +} + +/* + * kallsyms_symbol_next + * + * Parameters: + * prefix_name prefix of a symbol name to lookup + * flag 0 means search from the head, 1 means continue search. + * Returns: + * 1 if a symbol matches the given prefix. + * 0 if no string found + */ + +int kallsyms_symbol_next(char *prefix_name, int flag) +{ + int prefix_len = strlen(prefix_name); + static loff_t pos; + const char *name; + + if (!flag) + pos = 0; + + while ((name = kdb_walk_kallsyms(&pos))) { + if (strncmp(name, prefix_name, prefix_len) == 0) { + strncpy(prefix_name, name, strlen(name)+1); + return 1; + } + } + return 0; +} + +#if defined(CONFIG_SMP) +/* + * kdb_ipi + * + * This function is called from the non-maskable interrupt + * handler to handle a kdb IPI instruction. + * + * Inputs: + * regs = Exception frame pointer + * Outputs: + * None. + * Returns: + * 0 - Did not handle NMI + * 1 - Handled NMI + * Locking: + * None. + * Remarks: + * Initially one processor is invoked in the kdb() code. That + * processor sends an ipi which drives this routine on the other + * processors. All this does is call kdb() with reason SWITCH. + * This puts all processors into the kdb() routine and all the + * code for breakpoints etc. is in one place. + * One problem with the way the kdb NMI is sent, the NMI has no + * identification that says it came from kdb. If the cpu's kdb state is + * marked as "waiting for kdb_ipi" then the NMI is treated as coming from + * kdb, otherwise it is assumed to be for another reason and is ignored. + */ + +int +kdb_ipi(struct pt_regs *regs, void (*ack_interrupt)(void)) +{ + /* Do not print before checking and clearing WAIT_IPI, IPIs are + * going all the time. + */ + if (KDB_STATE(WAIT_IPI)) { + /* + * Stopping other processors via smp_kdb_stop(). + */ + if (ack_interrupt) + (*ack_interrupt)(); /* Acknowledge the interrupt */ + KDB_STATE_CLEAR(WAIT_IPI); + KDB_DEBUG_STATE("kdb_ipi 1", 0); + kdb(KDB_REASON_SWITCH, 0, regs); /* Spin in kdb() */ + KDB_DEBUG_STATE("kdb_ipi 2", 0); + return 1; + } + return 0; +} +#endif /* CONFIG_SMP */ + +/* + * kdb_symbol_print + * + * Standard method for printing a symbol name and offset. + * Inputs: + * addr Address to be printed. + * symtab Address of symbol data, if NULL this routine does its + * own lookup. + * punc Punctuation for string, bit field. + * Outputs: + * None. + * Returns: + * Always 0. + * Locking: + * none. + * Remarks: + * The string and its punctuation is only printed if the address + * is inside the kernel, except that the value is always printed + * when requested. + */ + +void +kdb_symbol_print(kdb_machreg_t addr, const kdb_symtab_t *symtab_p, unsigned int punc) +{ + kdb_symtab_t symtab, *symtab_p2; + if (symtab_p) { + symtab_p2 = (kdb_symtab_t *)symtab_p; + } + else { + symtab_p2 = &symtab; + kdbnearsym(addr, symtab_p2); + } + if (symtab_p2->sym_name || (punc & KDB_SP_VALUE)) { + ; /* drop through */ + } + else { + return; + } + if (punc & KDB_SP_SPACEB) { + kdb_printf(" "); + } + if (punc & KDB_SP_VALUE) { + kdb_printf(kdb_machreg_fmt0, addr); + } + if (symtab_p2->sym_name) { + if (punc & KDB_SP_VALUE) { + kdb_printf(" "); + } + if (punc & KDB_SP_PAREN) { + kdb_printf("("); + } + if (strcmp(symtab_p2->mod_name, "kernel")) { + kdb_printf("[%s]", symtab_p2->mod_name); + } + kdb_printf("%s", symtab_p2->sym_name); + if (addr != symtab_p2->sym_start) { + kdb_printf("+0x%lx", addr - symtab_p2->sym_start); + } + if (punc & KDB_SP_SYMSIZE) { + kdb_printf("/0x%lx", symtab_p2->sym_end - symtab_p2->sym_start); + } + if (punc & KDB_SP_PAREN) { + kdb_printf(")"); + } + } + if (punc & KDB_SP_SPACEA) { + kdb_printf(" "); + } + if (punc & KDB_SP_NEWLINE) { + kdb_printf("\n"); + } +} + +/* + * kdb_strdup + * + * kdb equivalent of strdup, for disasm code. + * Inputs: + * str The string to duplicate. + * type Flags to kmalloc for the new string. + * Outputs: + * None. + * Returns: + * Address of the new string, NULL if storage could not be allocated. + * Locking: + * none. + * Remarks: + * This is not in lib/string.c because it uses kmalloc which is not + * available when string.o is used in boot loaders. + */ + +char *kdb_strdup(const char *str, gfp_t type) +{ + int n = strlen(str)+1; + char *s = kmalloc(n, type); + if (!s) return NULL; + return strcpy(s, str); +} + +/* + * kdb_getarea_size + * + * Read an area of data. The kdb equivalent of copy_from_user, with + * kdb messages for invalid addresses. + * Inputs: + * res Pointer to the area to receive the result. + * addr Address of the area to copy. + * size Size of the area. + * Outputs: + * none. + * Returns: + * 0 for success, < 0 for error. + * Locking: + * none. + */ + +int kdb_getarea_size(void *res, unsigned long addr, size_t size) +{ + int ret = kdba_getarea_size(res, addr, size); + if (ret) { + if (!KDB_STATE(SUPPRESS)) { + kdb_printf("kdb_getarea: Bad address 0x%lx\n", addr); + KDB_STATE_SET(SUPPRESS); + } + ret = KDB_BADADDR; + } + else { + KDB_STATE_CLEAR(SUPPRESS); + } + return(ret); +} + +/* + * kdb_putarea_size + * + * Write an area of data. The kdb equivalent of copy_to_user, with + * kdb messages for invalid addresses. + * Inputs: + * addr Address of the area to write to. + * res Pointer to the area holding the data. + * size Size of the area. + * Outputs: + * none. + * Returns: + * 0 for success, < 0 for error. + * Locking: + * none. + */ + +int kdb_putarea_size(unsigned long addr, void *res, size_t size) +{ + int ret = kdba_putarea_size(addr, res, size); + if (ret) { + if (!KDB_STATE(SUPPRESS)) { + kdb_printf("kdb_putarea: Bad address 0x%lx\n", addr); + KDB_STATE_SET(SUPPRESS); + } + ret = KDB_BADADDR; + } + else { + KDB_STATE_CLEAR(SUPPRESS); + } + return(ret); +} + +/* + * kdb_getphys + * + * Read data from a physical address. Validate the address is in range, + * use kmap_atomic() to get data + * + * Similar to kdb_getarea() - but for phys addresses + * + * Inputs: + * res Pointer to the word to receive the result + * addr Physical address of the area to copy + * size Size of the area + * Outputs: + * none. + * Returns: + * 0 for success, < 0 for error. + * Locking: + * none. + */ +static int kdb_getphys(void *res, unsigned long addr, size_t size) +{ + unsigned long pfn; + void *vaddr; + struct page *page; + + pfn = (addr >> PAGE_SHIFT); + if (!pfn_valid(pfn)) + return 1; + page = pfn_to_page(pfn); + vaddr = kmap_atomic(page, KM_KDB); + memcpy(res, vaddr + (addr & (PAGE_SIZE -1)), size); + kunmap_atomic(vaddr, KM_KDB); + + return 0; +} + +/* + * kdb_getphysword + * + * Inputs: + * word Pointer to the word to receive the result. + * addr Address of the area to copy. + * size Size of the area. + * Outputs: + * none. + * Returns: + * 0 for success, < 0 for error. + * Locking: + * none. + */ +int kdb_getphysword(unsigned long *word, unsigned long addr, size_t size) +{ + int diag; + __u8 w1; + __u16 w2; + __u32 w4; + __u64 w8; + *word = 0; /* Default value if addr or size is invalid */ + + switch (size) { + case 1: + if (!(diag = kdb_getphys(&w1, addr, sizeof(w1)))) + *word = w1; + break; + case 2: + if (!(diag = kdb_getphys(&w2, addr, sizeof(w2)))) + *word = w2; + break; + case 4: + if (!(diag = kdb_getphys(&w4, addr, sizeof(w4)))) + *word = w4; + break; + case 8: + if (size <= sizeof(*word)) { + if (!(diag = kdb_getphys(&w8, addr, sizeof(w8)))) + *word = w8; + break; + } + /* drop through */ + default: + diag = KDB_BADWIDTH; + kdb_printf("kdb_getphysword: bad width %ld\n", (long) size); + } + return(diag); +} + +/* + * kdb_getword + * + * Read a binary value. Unlike kdb_getarea, this treats data as numbers. + * Inputs: + * word Pointer to the word to receive the result. + * addr Address of the area to copy. + * size Size of the area. + * Outputs: + * none. + * Returns: + * 0 for success, < 0 for error. + * Locking: + * none. + */ + +int kdb_getword(unsigned long *word, unsigned long addr, size_t size) +{ + int diag; + __u8 w1; + __u16 w2; + __u32 w4; + __u64 w8; + *word = 0; /* Default value if addr or size is invalid */ + switch (size) { + case 1: + if (!(diag = kdb_getarea(w1, addr))) + *word = w1; + break; + case 2: + if (!(diag = kdb_getarea(w2, addr))) + *word = w2; + break; + case 4: + if (!(diag = kdb_getarea(w4, addr))) + *word = w4; + break; + case 8: + if (size <= sizeof(*word)) { + if (!(diag = kdb_getarea(w8, addr))) + *word = w8; + break; + } + /* drop through */ + default: + diag = KDB_BADWIDTH; + kdb_printf("kdb_getword: bad width %ld\n", (long) size); + } + return(diag); +} + +/* + * kdb_putword + * + * Write a binary value. Unlike kdb_putarea, this treats data as numbers. + * Inputs: + * addr Address of the area to write to.. + * word The value to set. + * size Size of the area. + * Outputs: + * none. + * Returns: + * 0 for success, < 0 for error. + * Locking: + * none. + */ + +int kdb_putword(unsigned long addr, unsigned long word, size_t size) +{ + int diag; + __u8 w1; + __u16 w2; + __u32 w4; + __u64 w8; + switch (size) { + case 1: + w1 = word; + diag = kdb_putarea(addr, w1); + break; + case 2: + w2 = word; + diag = kdb_putarea(addr, w2); + break; + case 4: + w4 = word; + diag = kdb_putarea(addr, w4); + break; + case 8: + if (size <= sizeof(word)) { + w8 = word; + diag = kdb_putarea(addr, w8); + break; + } + /* drop through */ + default: + diag = KDB_BADWIDTH; + kdb_printf("kdb_putword: bad width %ld\n", (long) size); + } + return(diag); +} + +/* + * kdb_task_state_string + * + * Convert a string containing any of the letters DRSTCZEUIMA to a mask + * for the process state field and return the value. If no argument is + * supplied, return the mask that corresponds to environment variable PS, + * DRSTCZEU by default. + * Inputs: + * s String to convert + * Outputs: + * none. + * Returns: + * Mask for process state. + * Locking: + * none. + * Notes: + * The mask folds data from several sources into a single long value, so + * be carefull not to overlap the bits. TASK_* bits are in the LSB, + * special cases like UNRUNNABLE are in the MSB. As of 2.6.10-rc1 there + * is no overlap between TASK_* and EXIT_* but that may not always be + * true, so EXIT_* bits are shifted left 16 bits before being stored in + * the mask. + */ + +#define UNRUNNABLE (1UL << (8*sizeof(unsigned long) - 1)) /* unrunnable is < 0 */ +#define RUNNING (1UL << (8*sizeof(unsigned long) - 2)) +#define IDLE (1UL << (8*sizeof(unsigned long) - 3)) +#define DAEMON (1UL << (8*sizeof(unsigned long) - 4)) + +unsigned long +kdb_task_state_string(const char *s) +{ + long res = 0; + if (!s && !(s = kdbgetenv("PS"))) { + s = "DRSTCZEU"; /* default value for ps */ + } + while (*s) { + switch (*s) { + case 'D': res |= TASK_UNINTERRUPTIBLE; break; + case 'R': res |= RUNNING; break; + case 'S': res |= TASK_INTERRUPTIBLE; break; + case 'T': res |= TASK_STOPPED; break; + case 'C': res |= TASK_TRACED; break; + case 'Z': res |= EXIT_ZOMBIE << 16; break; + case 'E': res |= EXIT_DEAD << 16; break; + case 'U': res |= UNRUNNABLE; break; + case 'I': res |= IDLE; break; + case 'M': res |= DAEMON; break; + case 'A': res = ~0UL; break; + default: + kdb_printf("%s: unknown flag '%c' ignored\n", __FUNCTION__, *s); + break; + } + ++s; + } + return res; +} + +/* + * kdb_task_state_char + * + * Return the character that represents the task state. + * Inputs: + * p struct task for the process + * Outputs: + * none. + * Returns: + * One character to represent the task state. + * Locking: + * none. + */ + +char +kdb_task_state_char (const struct task_struct *p) +{ + int cpu = kdb_process_cpu(p); + struct kdb_running_process *krp = kdb_running_process + cpu; + char state = (p->state == 0) ? 'R' : + (p->state < 0) ? 'U' : + (p->state & TASK_UNINTERRUPTIBLE) ? 'D' : + (p->state & TASK_STOPPED) ? 'T' : + (p->state & TASK_TRACED) ? 'C' : + (p->exit_state & EXIT_ZOMBIE) ? 'Z' : + (p->exit_state & EXIT_DEAD) ? 'E' : + (p->state & TASK_INTERRUPTIBLE) ? 'S' : '?'; + if (p->pid == 0) { + /* Idle task. Is it really idle, apart from the kdb interrupt? */ + if (!kdb_task_has_cpu(p) || krp->irq_depth == 1) { + /* There is a corner case when the idle task takes an + * interrupt and dies in the interrupt code. It has an + * interrupt count of 1 but that did not come from kdb. + * This corner case can only occur on the initial cpu, + * all the others were entered via the kdb IPI. + */ + if (cpu != kdb_initial_cpu || KDB_STATE_CPU(KEYBOARD, cpu)) + state = 'I'; /* idle task */ + } + } + else if (!p->mm && state == 'S') { + state = 'M'; /* sleeping system daemon */ + } + return state; +} + +/* + * kdb_task_state + * + * Return true if a process has the desired state given by the mask. + * Inputs: + * p struct task for the process + * mask mask from kdb_task_state_string to select processes + * Outputs: + * none. + * Returns: + * True if the process matches at least one criteria defined by the mask. + * Locking: + * none. + */ + +unsigned long +kdb_task_state(const struct task_struct *p, unsigned long mask) +{ + char state[] = { kdb_task_state_char(p), '\0' }; + return (mask & kdb_task_state_string(state)) != 0; +} + +struct kdb_running_process kdb_running_process[NR_CPUS]; + +/* Save the state of a running process and invoke kdb_main_loop. This is + * invoked on the current process on each cpu (assuming the cpu is responding). + */ + +int +kdb_save_running(struct pt_regs *regs, kdb_reason_t reason, + kdb_reason_t reason2, int error, kdb_dbtrap_t db_result) +{ + struct kdb_running_process *krp = kdb_running_process + smp_processor_id(); + krp->p = current; + krp->regs = regs; + krp->seqno = kdb_seqno; + krp->irq_depth = hardirq_count() >> HARDIRQ_SHIFT; + kdba_save_running(&(krp->arch), regs); + return kdb_main_loop(reason, reason2, error, db_result, regs); +} + +/* + * kdb_unsave_running + * + * Reverse the effect of kdb_save_running. + * Inputs: + * regs struct pt_regs for the process + * Outputs: + * Updates kdb_running_process[] for this cpu. + * Returns: + * none. + * Locking: + * none. + */ + +void +kdb_unsave_running(struct pt_regs *regs) +{ + struct kdb_running_process *krp = kdb_running_process + smp_processor_id(); + kdba_unsave_running(&(krp->arch), regs); + krp->seqno = 0; +} + + +/* + * kdb_print_nameval + * + * Print a name and its value, converting the value to a symbol lookup + * if possible. + * Inputs: + * name field name to print + * val value of field + * Outputs: + * none. + * Returns: + * none. + * Locking: + * none. + */ + +void +kdb_print_nameval(const char *name, unsigned long val) +{ + kdb_symtab_t symtab; + kdb_printf(" %-11.11s ", name); + if (kdbnearsym(val, &symtab)) + kdb_symbol_print(val, &symtab, KDB_SP_VALUE|KDB_SP_SYMSIZE|KDB_SP_NEWLINE); + else + kdb_printf("0x%lx\n", val); +} + +static struct page * kdb_get_one_user_page(const struct task_struct *tsk, unsigned long start, + int len, int write) +{ + struct mm_struct *mm = tsk->mm; + unsigned int flags; + struct vm_area_struct * vma; + + /* shouldn't cross a page boundary. */ + if ((start & PAGE_MASK) != ((start+len) & PAGE_MASK)) + return NULL; + + /* we need to align start address to the current page boundy, PAGE_ALIGN + * aligns to next page boundry. + * FIXME: What about hugetlb? + */ + start = start & PAGE_MASK; + flags = write ? (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD); + + vma = find_extend_vma(mm, start); + + /* may be we can allow access to VM_IO pages inside KDB? */ + if (!vma || (vma->vm_flags & VM_IO) || !(flags & vma->vm_flags)) + return NULL; + + return follow_page(vma, start, write ? FOLL_WRITE : 0); +} + +int kdb_getuserarea_size(void *to, unsigned long from, size_t size) +{ + struct page *page; + void *vaddr; + + page = kdb_get_one_user_page(kdb_current_task, from, size, 0); + if (!page) + return size; + + vaddr = kmap_atomic(page, KM_KDB); + memcpy(to, vaddr+ (from & (PAGE_SIZE - 1)), size); + kunmap_atomic(vaddr, KM_KDB); + + return 0; +} + +int kdb_putuserarea_size(unsigned long to, void *from, size_t size) +{ + struct page *page; + void *vaddr; + + page = kdb_get_one_user_page(kdb_current_task, to, size, 1); + if (!page) + return size; + + vaddr = kmap_atomic(page, KM_KDB); + memcpy(vaddr+ (to & (PAGE_SIZE - 1)), from, size); + kunmap_atomic(vaddr, KM_KDB); + + return 0; +} + +/* Last ditch allocator for debugging, so we can still debug even when the + * GFP_ATOMIC pool has been exhausted. The algorithms are tuned for space + * usage, not for speed. One smallish memory pool, the free chain is always in + * ascending address order to allow coalescing, allocations are done in brute + * force best fit. + */ + +struct debug_alloc_header { + u32 next; /* offset of next header from start of pool */ + u32 size; + void *caller; +}; + +/* The memory returned by this allocator must be aligned, which means so must + * the header size. Do not assume that sizeof(struct debug_alloc_header) is a + * multiple of the alignment, explicitly calculate the overhead of this header, + * including the alignment. The rest of this code must not use sizeof() on any + * header or pointer to a header. + */ +#define dah_align 8 +#define dah_overhead ALIGN(sizeof(struct debug_alloc_header), dah_align) + +static u64 debug_alloc_pool_aligned[256*1024/dah_align]; /* 256K pool */ +static char *debug_alloc_pool = (char *)debug_alloc_pool_aligned; +static u32 dah_first, dah_first_call = 1, dah_used = 0, dah_used_max = 0; + +/* Locking is awkward. The debug code is called from all contexts, including + * non maskable interrupts. A normal spinlock is not safe in NMI context. Try + * to get the debug allocator lock, if it cannot be obtained after a second + * then give up. If the lock could not be previously obtained on this cpu then + * only try once. + * + * sparse has no annotation for "this function _sometimes_ acquires a lock", so + * fudge the acquire/release notation. + */ +static DEFINE_SPINLOCK(dap_lock); +static int +get_dap_lock(void) + __acquires(dap_lock) +{ + static int dap_locked = -1; + int count; + if (dap_locked == smp_processor_id()) + count = 1; + else + count = 1000; + while (1) { + if (spin_trylock(&dap_lock)) { + dap_locked = -1; + return 1; + } + if (!count--) + break; + udelay(1000); + } + dap_locked = smp_processor_id(); + __acquire(dap_lock); + return 0; +} + +void +*debug_kmalloc(size_t size, gfp_t flags) +{ + unsigned int rem, h_offset; + struct debug_alloc_header *best, *bestprev, *prev, *h; + void *p = NULL; + if (!get_dap_lock()) { + __release(dap_lock); /* we never actually got it */ + return NULL; + } + h = (struct debug_alloc_header *)(debug_alloc_pool + dah_first); + if (dah_first_call) { + h->size = sizeof(debug_alloc_pool_aligned) - dah_overhead; + dah_first_call = 0; + } + size = ALIGN(size, dah_align); + prev = best = bestprev = NULL; + while (1) { + if (h->size >= size && (!best || h->size < best->size)) { + best = h; + bestprev = prev; + if (h->size == size) + break; + } + if (!h->next) + break; + prev = h; + h = (struct debug_alloc_header *)(debug_alloc_pool + h->next); + } + if (!best) + goto out; + rem = best->size - size; + /* The pool must always contain at least one header */ + if (best->next == 0 && bestprev == NULL && rem < dah_overhead) + goto out; + if (rem >= dah_overhead) { + best->size = size; + h_offset = ((char *)best - debug_alloc_pool) + + dah_overhead + best->size; + h = (struct debug_alloc_header *)(debug_alloc_pool + h_offset); + h->size = rem - dah_overhead; + h->next = best->next; + } else + h_offset = best->next; + best->caller = __builtin_return_address(0); + dah_used += best->size; + dah_used_max = max(dah_used, dah_used_max); + if (bestprev) + bestprev->next = h_offset; + else + dah_first = h_offset; + p = (char *)best + dah_overhead; + memset(p, POISON_INUSE, best->size - 1); + *((char *)p + best->size - 1) = POISON_END; +out: + spin_unlock(&dap_lock); + return p; +} + +void +debug_kfree(void *p) +{ + struct debug_alloc_header *h; + unsigned int h_offset; + if (!p) + return; + if ((char *)p < debug_alloc_pool || + (char *)p >= debug_alloc_pool + sizeof(debug_alloc_pool_aligned)) { + kfree(p); + return; + } + if (!get_dap_lock()) { + __release(dap_lock); /* we never actually got it */ + return; /* memory leak, cannot be helped */ + } + h = (struct debug_alloc_header *)((char *)p - dah_overhead); + memset(p, POISON_FREE, h->size - 1); + *((char *)p + h->size - 1) = POISON_END; + h->caller = NULL; + dah_used -= h->size; + h_offset = (char *)h - debug_alloc_pool; + if (h_offset < dah_first) { + h->next = dah_first; + dah_first = h_offset; + } else { + struct debug_alloc_header *prev; + unsigned int prev_offset; + prev = (struct debug_alloc_header *)(debug_alloc_pool + dah_first); + while (1) { + if (!prev->next || prev->next > h_offset) + break; + prev = (struct debug_alloc_header *) + (debug_alloc_pool + prev->next); + } + prev_offset = (char *)prev - debug_alloc_pool; + if (prev_offset + dah_overhead + prev->size == h_offset) { + prev->size += dah_overhead + h->size; + memset(h, POISON_FREE, dah_overhead - 1); + *((char *)h + dah_overhead - 1) = POISON_END; + h = prev; + h_offset = prev_offset; + } else { + h->next = prev->next; + prev->next = h_offset; + } + } + if (h_offset + dah_overhead + h->size == h->next) { + struct debug_alloc_header *next; + next = (struct debug_alloc_header *) + (debug_alloc_pool + h->next); + h->size += dah_overhead + next->size; + h->next = next->next; + memset(next, POISON_FREE, dah_overhead - 1); + *((char *)next + dah_overhead - 1) = POISON_END; + } + spin_unlock(&dap_lock); +} + +void +debug_kusage(void) +{ + struct debug_alloc_header *h_free, *h_used; +#ifdef CONFIG_IA64 + /* FIXME: using dah for ia64 unwind always results in a memory leak. + * Fix that memory leak first, then set debug_kusage_one_time = 1 for + * all architectures. + */ + static int debug_kusage_one_time = 0; +#else + static int debug_kusage_one_time = 1; +#endif + if (!get_dap_lock()) { + __release(dap_lock); /* we never actually got it */ + return; + } + h_free = (struct debug_alloc_header *)(debug_alloc_pool + dah_first); + if (dah_first == 0 && + (h_free->size == sizeof(debug_alloc_pool_aligned) - dah_overhead || + dah_first_call)) + goto out; + if (!debug_kusage_one_time) + goto out; + debug_kusage_one_time = 0; + kdb_printf("%s: debug_kmalloc memory leak dah_first %d\n", + __FUNCTION__, dah_first); + if (dah_first) { + h_used = (struct debug_alloc_header *)debug_alloc_pool; + kdb_printf("%s: h_used %p size %d\n", __FUNCTION__, h_used, h_used->size); + } + do { + h_used = (struct debug_alloc_header *) + ((char *)h_free + dah_overhead + h_free->size); + kdb_printf("%s: h_used %p size %d caller %p\n", + __FUNCTION__, h_used, h_used->size, h_used->caller); + h_free = (struct debug_alloc_header *) + (debug_alloc_pool + h_free->next); + } while (h_free->next); + h_used = (struct debug_alloc_header *) + ((char *)h_free + dah_overhead + h_free->size); + if ((char *)h_used - debug_alloc_pool != + sizeof(debug_alloc_pool_aligned)) + kdb_printf("%s: h_used %p size %d caller %p\n", + __FUNCTION__, h_used, h_used->size, h_used->caller); +out: + spin_unlock(&dap_lock); +} + +/* Maintain a small stack of kdb_flags to allow recursion without disturbing + * the global kdb state. + */ + +static int kdb_flags_stack[4], kdb_flags_index; + +void +kdb_save_flags(void) +{ + BUG_ON(kdb_flags_index >= ARRAY_SIZE(kdb_flags_stack)); + kdb_flags_stack[kdb_flags_index++] = kdb_flags; +} + +void +kdb_restore_flags(void) +{ + BUG_ON(kdb_flags_index <= 0); + kdb_flags = kdb_flags_stack[--kdb_flags_index]; +} --- /dev/null +++ b/kdb/modules/Makefile @@ -0,0 +1,14 @@ +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (c) 1999-2006 Silicon Graphics, Inc. All Rights Reserved. +# + +obj-$(CONFIG_KDB_MODULES) += kdbm_pg.o kdbm_task.o kdbm_vm.o kdbm_sched.o +obj-m += kdbm_debugtypes.o +ifdef CONFIG_X86 +obj-$(CONFIG_KDB_MODULES) += kdbm_x86.o +endif +CFLAGS_kdbm_vm.o += -I $(srctree)/drivers/scsi --- /dev/null +++ b/kdb/modules/kdbm_debugtypes.c @@ -0,0 +1,388 @@ +/* this one has some additional address validation - untested */ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved. + */ + +/* + * + * Most of this code is borrowed and adapted from the lkcd command "lcrash" + * and its supporting libarary. + * + * This module provides kdb commands for casting memory structures. + * It loads symbolic debugging info (provided from lcrash -o), and provides + * "print" "px", "pd" + * (this information originally comes from the lcrash "kerntypes" file) + * + * A key here is tacking a file of debug info onto this module, for + * load with it at insmod time. + * + * Careful of porting the klib KL_XXX functions (they call thru a jump table + * that we don't use here) + * + * Usage: + * in order for the insmod kdbm_debugtypes.ko to succeed in loading types + * you must first use lcrash -t kerntypes.xxxx -o debug_info + * and echo debug_info > /proc/kdb/debug_info_name + */ + +#define VMALLOC_START_IA64 0xa000000200000000 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lcrash/klib.h" +#include "lcrash/kl_stringtab.h" +#include "lcrash/kl_btnode.h" +#include "lcrash/lc_eval.h" + +MODULE_AUTHOR("SGI"); +MODULE_DESCRIPTION("Load symbolic debugging information"); +MODULE_LICENSE("GPL"); + +#undef next_node /* collision with nodemask.h */ +static char *stringstorage, **stringp_array; +static void *filestorage; +static long num_strings, num_kltypes, num_dsyms, stringstorage_size; +extern int have_debug_file; +extern dbg_sym_t *types_tree_head; +extern dbg_sym_t *typedefs_tree_head; +extern kltype_t *kltype_array; +extern dbg_sym_t *dsym_types_array; +extern dbg_sym_t *type_tree; +extern dbg_sym_t *typedef_tree; + +/* + * use a pointer's value as an index in the stringp_array (num_strings) and + * translate it to string address + * + * Return 0 for success, 1 for failure + */ +static int +index_to_char_ptr(char **ptrp) +{ + long i; + + i = (long)*ptrp; + /* we use a value of -1 to mean this was a null pointer */ + if (i == -1) { + *ptrp = NULL; + return 0; + } + if (i > num_strings-1) { + printk("Could not translate character string index %#lx\n", i); + return 1; + } + *ptrp = *(stringp_array+i); + return 0; +} + +/* + * use a pointer's value as an index in the kltype_array (num_kltypes) and + * translate it to the kltype_t address + * + * return 0 for success, 1 for failure + */ +static int +index_to_kltype_ptr(kltype_t **ptrp) +{ + long i; + + i = (long)*ptrp; + /* we use a value of -1 to mean this was a null pointer */ + if (i == -1) { + *ptrp = NULL; + return 0; + } + if (i > num_kltypes-1) { + printk("Could not translate kl_type string index %#lx\n", i); + return 1; + } + *ptrp = kltype_array+i; + return 0; +} + +/* + * look up a pointer in the dsym_types_array (num_dsyms) and + * translate it to the index in the array + * + * return 0 for success, 1 for failure + */ +static int +index_to_dbg_ptr(dbg_sym_t **ptrp) +{ + long i; + + i = (long)*ptrp; + /* we use a value of -1 to mean this was a null pointer */ + if (i == -1) { + *ptrp = NULL; + return 0; + } + if (i > num_dsyms-1) { + printk("Could not translate dbg_sym_t index %#lx\n", i); + return 1; + } + *ptrp = dsym_types_array+i; + return 0; +} + + +/* + * Work on the image of the file built by lcrash. + * Unpack the strings, and resolve the pointers in the arrays of kltype_t's + * and dbg_sym_t's to pointers. + * + * see lcrash's lib/libklib/kl_debug.c, which generates this file + * + * Return the pointers to the heads of the two binary trees by means of + * pointer arguments. + * + * Return 0 for sucess, 1 for any error. + */ +static int +trans_file_image(void *file_storage, long file_size, dbg_sym_t **type_treepp, + dbg_sym_t **typedef_treepp) +{ + int len; + long i, section_size, *lp, element_size; + long head_types_tree, head_typedefs_tree; + char *ptr, *stringsection, *kltypesection, *dbgsection; + void *kltypestorage, *dbgstorage; + kltype_t *klp; + dbg_sym_t *dbgp; + + /* 1) the strings */ + lp = (long *)file_storage; + stringsection = (char *)lp; + section_size = *lp++; + num_strings = *lp++; + lp++; /* element size does not apply the strings section */ + + stringstorage_size = section_size - (3*sizeof(long)); + stringstorage = (char *)lp; + + stringp_array = (char **)vmalloc(num_strings * sizeof(char *)); + if (! stringp_array) { + printk("vmalloc of %ld string pointers failed\n", num_strings); + return 1; + } + ptr = stringstorage; + for (i=0; ikl_name)) + goto bad; + if (index_to_char_ptr(&klp->kl_typestr)) + goto bad; + if (index_to_kltype_ptr(&klp->kl_member)) + goto bad; + if (index_to_kltype_ptr(&klp->kl_next)) + goto bad; + if (index_to_kltype_ptr(&klp->kl_realtype)) + goto bad; + if (index_to_kltype_ptr(&klp->kl_indextype)) + goto bad; + if (index_to_kltype_ptr(&klp->kl_elementtype)) + goto bad; + if (index_to_dbg_ptr((dbg_sym_t **)&klp->kl_ptr)) + goto bad; + } + + /* translate the indices in our our array of dbg_sym_t's to pointers */ + /* (see write_dbgtype() for the fields that can be translated) */ + dbgp = dsym_types_array; + for (i=0; isym_bt.bt_key)) + goto bad; + if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_bt.bt_left)) + goto bad; + if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_bt.bt_right)) + goto bad; + if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_bt.bt_parent)) + goto bad; + if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_next)) + goto bad; + if (index_to_dbg_ptr((dbg_sym_t **)&dbgp->sym_link)) + goto bad; + if (index_to_kltype_ptr(&dbgp->sym_kltype)) + goto bad; + } + + vfree(stringp_array); + return 0; +bad: + printk("trans_file_image() returning an error\n"); + vfree(stringp_array); + return 1; +} + +/* there is /proc interface to this string */ +extern char kdb_debug_info_filename[]; +/* + * This is the module initialization function. + */ +static int __init +kdbm_debuginfo_init(void) +{ + int len; + long ret, file_size; + ssize_t sizeread; + mm_segment_t fs; + struct file *file; + loff_t inode_size, pos; + + len = strlen(kdb_debug_info_filename); + if (!len) { + printk("kdb: no file name in /proc/kdb/debug_info_name\n"); + return -ENODEV; + } + + fs = get_fs(); /* save previous value of address limits */ + set_fs (get_ds()); /* use kernel limit */ + + file = filp_open(kdb_debug_info_filename, O_RDONLY, 0); + if (IS_ERR(file)) { + set_fs(fs); + printk ( + "kdb: open of %s (from /proc/kdb/debug_info_name) failed\n", + kdb_debug_info_filename); + return -ENODEV; + } + if (!file->f_op || (!file->f_op->read && !file->f_op->llseek)) { + printk ("file has no operation for read or seek\n"); + set_fs(fs); + return -ENODEV; + } + inode_size = file->f_dentry->d_inode->i_size; + + /* + * File has a header word on it that contains the size of the + * file. We don't need it, but can use it as a sanity check. + */ + pos = 0; + sizeread = file->f_op->read(file, (char *)&file_size, + sizeof(file_size), &pos); + if (sizeread != sizeof(file_size)) { + printk("could not read %d bytes from %s\n", + (int)sizeof(file_size), kdb_debug_info_filename); + ret = filp_close(file, NULL); + set_fs(fs); + return -ENODEV; + } + if (inode_size != file_size) { + printk("file says %ld, inode says %lld\n", + file_size, inode_size); + ret = filp_close(file, NULL); + set_fs(fs); + return -ENODEV; + } + + /* space for the rest of the file: */ + file_size -= sizeof(long); + filestorage = (void *)vmalloc(file_size); + + pos = sizeof(file_size); /* position after the header word */ + sizeread = file->f_op->read(file, (char *)filestorage, + file_size, &pos); + if (sizeread != file_size) { + printk("could not read %ld bytes from %s\n", + file_size, kdb_debug_info_filename); + ret = filp_close(file, NULL); + set_fs(fs); + vfree (filestorage); + return -ENODEV; + } + + ret = filp_close(file, NULL); + set_fs(fs); /* restore address limits before returning to user space */ + + if (trans_file_image(filestorage, file_size, &types_tree_head, + &typedefs_tree_head)){ + vfree (filestorage); + return -ENODEV; + } + printk("kdbm_debuginfo loaded %s\n", kdb_debug_info_filename); + /* set the lcrash code's binary tree head nodes */ + type_tree = types_tree_head; + typedef_tree = typedefs_tree_head; + + have_debug_file = 1; + + return 0; +} + +/* + * This is the module exit function. + */ +static void __exit +kdbm_debuginfo_exit(void) +{ + printk("kdbm_debuginfo unloaded %s\n", kdb_debug_info_filename); + vfree (filestorage); + have_debug_file = 0; + return; +} + +module_init(kdbm_debuginfo_init); +module_exit(kdbm_debuginfo_exit); --- /dev/null +++ b/kdb/modules/kdbm_pg.c @@ -0,0 +1,684 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("SGI"); +MODULE_DESCRIPTION("Debug page information"); +MODULE_LICENSE("GPL"); + +/* Standard Linux page stuff */ + +#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_NUMA) +/* From include/linux/page-flags.h */ +static char *pg_flag_vals[] = { + "PG_locked", "PG_error", "PG_referenced", "PG_uptodate", + "PG_dirty", "PG_lru", "PG_active", "PG_slab", + "PG_owner_priv_1", "PG_arch_1", "PG_reserved", "PG_private", + "PG_writeback", +#ifdef CONFIG_PAGEFLAGS_EXTENDED + "PG_head", "PG_tail", +#else + "PG_compound", +#endif + "PG_swapcache", "PG_mappedtodisk", "PG_reclaim", "PG_buddy", +#ifdef CONFIG_IA64_UNCACHED_ALLOCATOR + "PG_uncached", +#endif + NULL }; +#endif + +/* From include/linux/buffer_head.h */ +static char *bh_state_vals[] = { + "Uptodate", "Dirty", "Lock", "Req", + "Uptodate_Lock", "Mapped", "New", "Async_read", + "Async_write", "Delay", "Boundary", "Write_EIO", + "Ordered", "Eopnotsupp", "Unwritten", "PriavateStart", + NULL }; + +/* From include/linux/bio.h */ +static char *bio_flag_vals[] = { + "Uptodate", "RW_block", "EOF", "Seg_valid", + "Cloned", "Bounced", "User_mapped", "Eopnotsupp", + NULL }; + +/* From include/linux/fs.h */ +static char *inode_flag_vals[] = { + "I_DIRTY_SYNC", "I_DIRTY_DATASYNC", "I_DIRTY_PAGES", "I_NEW", + "I_WILL_FREE", "I_FREEING", "I_CLEAR", "I_LOCK", + "I_SYNC", NULL }; + +static char *map_flags(unsigned long flags, char *mapping[]) +{ + static char buffer[256]; + int index; + int offset = 12; + + buffer[0] = '\0'; + + for (index = 0; flags && mapping[index]; flags >>= 1, index++) { + if (flags & 1) { + if ((offset + strlen(mapping[index]) + 1) >= 80) { + strcat(buffer, "\n "); + offset = 12; + } else if (offset > 12) { + strcat(buffer, " "); + offset++; + } + strcat(buffer, mapping[index]); + offset += strlen(mapping[index]); + } + } + + return (buffer); +} + +static int +kdbm_buffers(int argc, const char **argv) +{ + struct buffer_head bh; + unsigned long addr; + long offset = 0; + int nextarg; + int diag; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) || + (diag = kdb_getarea(bh, addr))) + return(diag); + + kdb_printf("buffer_head at 0x%lx\n", addr); + kdb_printf(" bno %llu size %llu dev 0x%x\n", + (unsigned long long)bh.b_blocknr, + (unsigned long long)bh.b_size, + bh.b_bdev ? bh.b_bdev->bd_dev : 0); + kdb_printf(" count %d state 0x%lx [%s]\n", + bh.b_count.counter, bh.b_state, + map_flags(bh.b_state, bh_state_vals)); + kdb_printf(" b_data 0x%p\n", + bh.b_data); + kdb_printf(" b_page 0x%p b_this_page 0x%p b_private 0x%p\n", + bh.b_page, bh.b_this_page, bh.b_private); + kdb_printf(" b_end_io "); + if (bh.b_end_io) + kdb_symbol_print(kdba_funcptr_value(bh.b_end_io), NULL, KDB_SP_VALUE); + else + kdb_printf("(NULL)"); + kdb_printf("\n"); + + return 0; +} + +static int +print_biovec(struct bio_vec *vec, int vcount) +{ + struct bio_vec bvec; + unsigned long addr; + int diag; + int i; + + if (vcount < 1 || vcount > BIO_MAX_PAGES) { + kdb_printf(" [skipped iovecs, vcnt is %d]\n", vcount); + return 0; + } + + addr = (unsigned long)vec; + for (i = 0; i < vcount; i++) { + if ((diag = kdb_getarea(bvec, addr))) + return(diag); + addr += sizeof(bvec); + kdb_printf(" [%d] page 0x%p length=%u offset=%u\n", + i, bvec.bv_page, bvec.bv_len, bvec.bv_offset); + } + return 0; +} + +static int +kdbm_bio(int argc, const char **argv) +{ + struct bio bio; + unsigned long addr; + long offset = 0; + int nextarg; + int diag; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) || + (diag = kdb_getarea(bio, addr))) + return(diag); + + kdb_printf("bio at 0x%lx\n", addr); + kdb_printf(" bno %llu next 0x%p dev 0x%x\n", + (unsigned long long)bio.bi_sector, + bio.bi_next, bio.bi_bdev ? bio.bi_bdev->bd_dev : 0); + kdb_printf(" vcnt %u vec 0x%p rw 0x%lx flags 0x%lx [%s]\n", + bio.bi_vcnt, bio.bi_io_vec, bio.bi_rw, bio.bi_flags, + map_flags(bio.bi_flags, bio_flag_vals)); + print_biovec(bio.bi_io_vec, bio.bi_vcnt); + kdb_printf(" count %d private 0x%p\n", + atomic_read(&bio.bi_cnt), bio.bi_private); + kdb_printf(" bi_end_io "); + if (bio.bi_end_io) + kdb_symbol_print(kdba_funcptr_value(bio.bi_end_io), NULL, KDB_SP_VALUE); + else + kdb_printf("(NULL)"); + kdb_printf("\n"); + + return 0; +} + +#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_NUMA) +static char *page_flags(unsigned long flags) +{ + return(map_flags(flags, pg_flag_vals)); +} + +static int +kdbm_page(int argc, const char **argv) +{ + struct page page; + 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); + if (diag) + return diag; + +#ifdef __ia64__ + if (rgn_index(addr) == 0) + addr = (unsigned long) &mem_map[addr]; /* assume region 0 is a page index, not an address */ +#else + if (addr < PAGE_OFFSET) + addr = (unsigned long) &mem_map[addr]; +#endif + + if ((diag = kdb_getarea(page, addr))) + return(diag); + + kdb_printf("struct page at 0x%lx\n", addr); + kdb_printf(" addr space 0x%p index %lu (offset 0x%llx)\n", + page.mapping, page.index, + (unsigned long long)page.index << PAGE_CACHE_SHIFT); + kdb_printf(" count %d flags %s\n", + page._count.counter, page_flags(page.flags)); + kdb_printf(" virtual 0x%p\n", page_address((struct page *)addr)); + if (page_has_buffers(&page)) + kdb_printf(" buffers 0x%p\n", page_buffers(&page)); + else + kdb_printf(" private 0x%lx\n", page_private(&page)); + + return 0; +} +#endif /* !CONFIG_DISCONTIGMEM && !NUMA */ + +static unsigned long +print_request(unsigned long addr) +{ + struct request rq; + + if (kdb_getarea(rq, addr)) + return(0); + + kdb_printf("struct request at 0x%lx\n", addr); + kdb_printf(" errors %d sector %llu nr_sectors %llu\n", + rq.errors, + (unsigned long long)blk_rq_pos(&rq), + (unsigned long long)blk_rq_sectors(&rq)); + + return (unsigned long) rq.queuelist.next; +} + +static int +kdbm_request(int argc, const char **argv) +{ + long offset = 0; + unsigned long addr; + int nextarg; + int diag; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); + if (diag) + return diag; + + print_request(addr); + return 0; +} + + +static int +kdbm_rqueue(int argc, const char **argv) +{ + struct request_queue rq; + unsigned long addr, head_addr, next; + long offset = 0; + int nextarg; + int i, diag; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) || + (diag = kdb_getarea(rq, addr))) + return(diag); + + kdb_printf("struct request_queue at 0x%lx\n", addr); + i = 0; + next = (unsigned long)rq.queue_head.next; + head_addr = addr + offsetof(struct request_queue, queue_head); + kdb_printf(" request queue: %s\n", next == head_addr ? + "empty" : ""); + while (next != head_addr) { + i++; + next = print_request(next); + } + + if (i) + kdb_printf("%d requests found\n", i); + + return 0; +} + + +static void +do_buffer(unsigned long addr) +{ + struct buffer_head bh; + + if (kdb_getarea(bh, addr)) + return; + + kdb_printf("\tbh 0x%lx bno %8llu [%s]\n", addr, + (unsigned long long)bh.b_blocknr, + map_flags(bh.b_state, bh_state_vals)); +} + +static void +kdbm_show_page(struct page *page, int first) +{ + if (first) + kdb_printf("page_struct index cnt zone nid flags\n"); + kdb_printf("%p%s %6lu %5d %3d %3d 0x%lx", + page_address(page), sizeof(void *) == 4 ? " " : "", + page->index, atomic_read(&(page->_count)), + page_zonenum(page), page_to_nid(page), + page->flags & (~0UL >> ZONES_SHIFT)); +#define kdb_page_flags(page, type) if (Page ## type(page)) kdb_printf(" " #type); + kdb_page_flags(page, Locked); + kdb_page_flags(page, Error); + kdb_page_flags(page, Referenced); + kdb_page_flags(page, Uptodate); + kdb_page_flags(page, Dirty); + kdb_page_flags(page, LRU); + kdb_page_flags(page, Active); + kdb_page_flags(page, Slab); + kdb_page_flags(page, Checked); + if (page->flags & (1UL << PG_arch_1)) + kdb_printf(" arch_1"); + kdb_page_flags(page, Reserved); + kdb_page_flags(page, Private); + kdb_page_flags(page, Writeback); + kdb_page_flags(page, Compound); + kdb_page_flags(page, SwapCache); + kdb_page_flags(page, MappedToDisk); + kdb_page_flags(page, Reclaim); + kdb_page_flags(page, Buddy); + + /* PageHighMem is not a flag any more, but treat it as one */ + kdb_page_flags(page, HighMem); + + if (page_has_buffers(page)) { + struct buffer_head *head, *bh; + kdb_printf("\n"); + head = bh = page_buffers(page); + do { + do_buffer((unsigned long) bh); + } while ((bh = bh->b_this_page) != head); + } else if (page_private(page)) { + kdb_printf(" private= 0x%lx", page_private(page)); + } + /* Cannot use page_mapping(page) here, it needs swapper_space which is + * not exported. + */ + if (page->mapping) + kdb_printf(" mapping= %p", page->mapping); + kdb_printf("\n"); +#undef kdb_page_flags +} + +static int +kdbm_inode_pages(int argc, const char **argv) +{ + struct inode *inode = NULL; + struct address_space *ap = NULL; + unsigned long addr, addr1 = 0; + long offset = 0; + int nextarg; + int diag; + pgoff_t next = 0; + struct page *page; + int first; + + nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); + if (diag) + goto out; + + if (argc == 2) { + nextarg = 2; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr1, + &offset, NULL); + if (diag) + goto out; + kdb_printf("Looking for page index 0x%lx ... \n", addr1); + next = addr1; + } + + if (!(inode = kmalloc(sizeof(*inode), GFP_ATOMIC))) { + kdb_printf("kdbm_inode_pages: cannot kmalloc inode\n"); + goto out; + } + if (!(ap = kmalloc(sizeof(*ap), GFP_ATOMIC))) { + kdb_printf("kdbm_inode_pages: cannot kmalloc ap\n"); + goto out; + } + if ((diag = kdb_getarea(*inode, addr))) + goto out; + if (!inode->i_mapping) { + kdb_printf("inode has no mapping\n"); + goto out; + } + if ((diag = kdb_getarea(*ap, (unsigned long) inode->i_mapping))) + goto out; + + /* Run the pages in the radix tree, printing the state of each page */ + first = 1; + while (radix_tree_gang_lookup(&ap->page_tree, (void **)&page, next, 1)) { + kdbm_show_page(page, first); + if (addr1) + break; + first = 0; + next = page->index + 1; + } + +out: + if (inode) + kfree(inode); + if (ap) + kfree(ap); + return diag; +} + +static int +kdbm_inode(int argc, const char **argv) +{ + struct inode *inode = NULL; + unsigned long addr; + unsigned char *iaddr; + long offset = 0; + int nextarg; + int diag; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL))) + goto out; + if (!(inode = kmalloc(sizeof(*inode), GFP_ATOMIC))) { + kdb_printf("kdbm_inode: cannot kmalloc inode\n"); + goto out; + } + if ((diag = kdb_getarea(*inode, addr))) + goto out; + + kdb_printf("struct inode at 0x%lx\n", addr); + + kdb_printf(" i_ino = %lu i_count = %u i_size %Ld\n", + inode->i_ino, atomic_read(&inode->i_count), + inode->i_size); + + kdb_printf(" i_mode = 0%o i_nlink = %d i_rdev = 0x%x\n", + inode->i_mode, inode->i_nlink, + inode->i_rdev); + + kdb_printf(" i_hash.nxt = 0x%p i_hash.pprev = 0x%p\n", + inode->i_hash.next, + inode->i_hash.pprev); + + kdb_printf(" i_list.nxt = 0x%p i_list.prv = 0x%p\n", + list_entry(inode->i_list.next, struct inode, i_list), + list_entry(inode->i_list.prev, struct inode, i_list)); + + kdb_printf(" i_dentry.nxt = 0x%p i_dentry.prv = 0x%p\n", + list_entry(inode->i_dentry.next, struct dentry, d_alias), + list_entry(inode->i_dentry.prev, struct dentry, d_alias)); + + kdb_printf(" i_sb = 0x%p i_op = 0x%p i_data = 0x%lx nrpages = %lu\n", + inode->i_sb, inode->i_op, + addr + offsetof(struct inode, i_data), + inode->i_data.nrpages); + kdb_printf(" i_fop= 0x%p i_flock = 0x%p i_mapping = 0x%p\n", + inode->i_fop, inode->i_flock, inode->i_mapping); + + kdb_printf(" i_flags 0x%x i_state 0x%lx [%s]", + inode->i_flags, inode->i_state, + map_flags(inode->i_state, inode_flag_vals)); + + iaddr = (char *)addr; + iaddr += offsetof(struct inode, i_private); + + kdb_printf(" fs specific info @ 0x%p\n", iaddr); +out: + if (inode) + kfree(inode); + return diag; +} + +static int +kdbm_sb(int argc, const char **argv) +{ + struct super_block *sb = NULL; + unsigned long addr; + long offset = 0; + int nextarg; + int diag; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL))) + goto out; + if (!(sb = kmalloc(sizeof(*sb), GFP_ATOMIC))) { + kdb_printf("kdbm_sb: cannot kmalloc sb\n"); + goto out; + } + if ((diag = kdb_getarea(*sb, addr))) + goto out; + + kdb_printf("struct super_block at 0x%lx\n", addr); + kdb_printf(" s_dev 0x%x blocksize 0x%lx\n", sb->s_dev, sb->s_blocksize); + kdb_printf(" s_flags 0x%lx s_root 0x%p\n", sb->s_flags, sb->s_root); + kdb_printf(" s_frozen %d s_id [%s]\n", sb->s_frozen, sb->s_id); +out: + if (sb) + kfree(sb); + return diag; +} + + +#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_NUMA) +/* According to Steve Lord, this code is ix86 specific. Patches to extend it to + * other architectures will be greatefully accepted. + */ +static int +kdbm_memmap(int argc, const char **argv) +{ + struct page page; + int i, page_count; + int slab_count = 0; + int dirty_count = 0; + int locked_count = 0; + int page_counts[10]; /* [8] = large counts, [9] = -1 counts */ + int buffered_count = 0; +#ifdef buffer_delay + int delay_count = 0; +#endif + int diag; + unsigned long addr; +#ifdef CONFIG_DISCONTIGMEM + int node_id = -1, found_node = 0; + int tot_page_count = 0; + unsigned long unode_id; + pg_data_t *pgdat; + + if (argc == 1) { /* node_id was specified */ + diag = kdbgetularg(argv[argc], &unode_id); + if (diag) + return diag; + node_id = (int)unode_id; + } + else if (argc) + return KDB_ARGCOUNT; + + tot_page_count = 0; + memset(page_counts, 0, sizeof(page_counts)); + + for_each_online_pgdat(pgdat) { + if ((node_id != -1) && (pgdat->node_id != node_id)) + continue; + found_node = 1; + addr = (unsigned long)pgdat->node_mem_map; + page_count = pgdat->node_spanned_pages; + tot_page_count += page_count; +#else + addr = (unsigned long)mem_map; + page_count = max_mapnr; + memset(page_counts, 0, sizeof(page_counts)); +#endif + for (i = 0; i < page_count; i++) { + if ((diag = kdb_getarea(page, addr))) + return(diag); + addr += sizeof(page); + + if (PageSlab(&page)) + slab_count++; + if (PageDirty(&page)) + dirty_count++; + if (PageLocked(&page)) + locked_count++; + if (page._count.counter == -1) + page_counts[9]++; + else if (page._count.counter < 8) + page_counts[page._count.counter]++; + else + page_counts[8]++; + if (page_has_buffers(&page)) { + buffered_count++; +#ifdef buffer_delay + if (buffer_delay(page.buffers)) + delay_count++; +#endif + } + } +#ifdef CONFIG_DISCONTIGMEM + } + page_count = tot_page_count; + if (node_id != -1) { + if (!found_node) { + kdb_printf("Node %d does not exist.\n", node_id); + return 0; + } + kdb_printf("Node %d pages:\n", node_id); + } +#endif + kdb_printf(" Total pages: %6d\n", page_count); + kdb_printf(" Slab pages: %6d\n", slab_count); + kdb_printf(" Dirty pages: %6d\n", dirty_count); + kdb_printf(" Locked pages: %6d\n", locked_count); + kdb_printf(" Buffer pages: %6d\n", buffered_count); +#ifdef buffer_delay + kdb_printf(" Delalloc pages: %6d\n", delay_count); +#endif + kdb_printf(" -1 page count: %6d\n", page_counts[9]); + for (i = 0; i < 8; i++) { + kdb_printf(" %d page count: %6d\n", + i, page_counts[i]); + } + kdb_printf(" high page count: %6d\n", page_counts[8]); + return 0; +} +#endif /* !CONFIG_DISCONTIGMEM && !NUMA */ + +static int __init kdbm_pg_init(void) +{ +#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_NUMA) + kdb_register("page", kdbm_page, "", "Display page", 0); +#endif + kdb_register("inode", kdbm_inode, "", "Display inode", 0); + kdb_register("sb", kdbm_sb, "", "Display super_block", 0); + kdb_register("bh", kdbm_buffers, "", "Display buffer", 0); + kdb_register("bio", kdbm_bio, "", "Display bio", 0); + kdb_register("inode_pages", kdbm_inode_pages, "", "Display pages in an inode", 0); + kdb_register("req", kdbm_request, "", "dump request struct", 0); + kdb_register("rqueue", kdbm_rqueue, "", "dump request queue", 0); +#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_NUMA) + kdb_register("memmap", kdbm_memmap, "", "page table summary", 0); +#endif + + return 0; +} + + +static void __exit kdbm_pg_exit(void) +{ +#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_NUMA) + kdb_unregister("page"); +#endif + kdb_unregister("inode"); + kdb_unregister("sb"); + kdb_unregister("bh"); + kdb_unregister("bio"); + kdb_unregister("inode_pages"); + kdb_unregister("req"); + kdb_unregister("rqueue"); +#if !defined(CONFIG_DISCONTIGMEM) && !defined(CONFIG_NUMA) + kdb_unregister("memmap"); +#endif +} + +module_init(kdbm_pg_init) +module_exit(kdbm_pg_exit) --- /dev/null +++ b/kdb/modules/kdbm_sched.c @@ -0,0 +1,57 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("SGI"); +MODULE_DESCRIPTION("Debug scheduler information"); +MODULE_LICENSE("GPL"); + +static int +kdbm_runqueues(int argc, const char **argv) +{ + unsigned long cpu; + int ret = 0; + + if (argc == 1) { + ret = kdbgetularg((char *)argv[1], &cpu); + if (!ret) { + if (!cpu_online(cpu)) { + kdb_printf("Invalid cpu number\n"); + } else + kdb_runqueue(cpu, kdb_printf); + } + } else if (argc == 0) { + for_each_online_cpu(cpu) + kdb_runqueue(cpu, kdb_printf); + } else { + /* More than one arg */ + kdb_printf("Specify one cpu number\n"); + } + return ret; +} + +static int __init kdbm_sched_init(void) +{ + kdb_register("rq", kdbm_runqueues, "", "Display runqueue for ", 0); + kdb_register("rqa", kdbm_runqueues, "", "Display all runqueues", 0); + return 0; +} + +static void __exit kdbm_sched_exit(void) +{ + kdb_unregister("rq"); + kdb_unregister("rqa"); +} + +module_init(kdbm_sched_init) +module_exit(kdbm_sched_exit) --- /dev/null +++ b/kdb/modules/kdbm_task.c @@ -0,0 +1,196 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 1999-2006 Silicon Graphics, Inc. All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("SGI"); +MODULE_DESCRIPTION("Debug struct task and sigset information"); +MODULE_LICENSE("GPL"); + +static char * +kdb_cpus_allowed_string(struct task_struct *tp) +{ + static char maskbuf[NR_CPUS * 8]; + if (cpus_equal(tp->cpus_allowed, cpu_online_map)) + strcpy(maskbuf, "ALL"); + else if (cpus_empty(tp->cpus_allowed)) + strcpy(maskbuf, "NONE"); + else if (cpus_weight(tp->cpus_allowed) == 1) + snprintf(maskbuf, sizeof(maskbuf), "ONLY(%d)", first_cpu(tp->cpus_allowed)); + else + cpulist_scnprintf(maskbuf, sizeof(maskbuf), &tp->cpus_allowed); + return maskbuf; +} + +static int +kdbm_task(int argc, const char **argv) +{ + unsigned long addr; + long offset=0; + int nextarg; + int e = 0; + struct task_struct *tp = NULL, *tp1; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + if ((e = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) != 0) + return(e); + + if (!(tp = kmalloc(sizeof(*tp), GFP_ATOMIC))) { + kdb_printf("%s: cannot kmalloc tp\n", __FUNCTION__); + goto out; + } + if ((e = kdb_getarea(*tp, addr))) { + kdb_printf("%s: invalid task address\n", __FUNCTION__); + goto out; + } + + tp1 = (struct task_struct *)addr; + kdb_printf( + "struct task at 0x%lx, pid=%d flags=0x%x state=%ld comm=\"%s\"\n", + addr, tp->pid, tp->flags, tp->state, tp->comm); + + kdb_printf(" cpu=%d policy=%u ", kdb_process_cpu(tp), tp->policy); + kdb_printf( + "prio=%d static_prio=%d cpus_allowed=", + tp->prio, tp->static_prio); + { + /* The cpus allowed string may be longer than kdb_printf() can + * handle. Print it in chunks. + */ + char c, *p; + p = kdb_cpus_allowed_string(tp); + while (1) { + if (strlen(p) < 100) { + kdb_printf("%s", p); + break; + } + c = p[100]; + p[100] = '\0'; + kdb_printf("%s", p); + p[100] = c; + p += 100; + } + } + kdb_printf(" &thread=0x%p\n", &tp1->thread); + + kdb_printf(" need_resched=%d ", + test_tsk_thread_flag(tp, TIF_NEED_RESCHED)); + kdb_printf( + "time_slice=%u", + tp->rt.time_slice); + kdb_printf(" lock_depth=%d\n", tp->lock_depth); + + kdb_printf( + " fs=0x%p files=0x%p mm=0x%p\n", + tp->fs, tp->files, tp->mm); + + if (tp->sysvsem.undo_list) + kdb_printf( + " sysvsem.sem_undo refcnt %d list_proc=0x%p\n", + atomic_read(&tp->sysvsem.undo_list->refcnt), + &tp->sysvsem.undo_list->list_proc); + + kdb_printf( + " signal=0x%p &blocked=0x%p &pending=0x%p\n", + tp->signal, &tp1->blocked, &tp1->pending); + + kdb_printf( + " utime=%ld stime=%ld cutime=%ld cstime=%ld\n", + tp->utime, tp->stime, + tp->signal ? tp->signal->cutime : 0L, + tp->signal ? tp->signal->cstime : 0L); + + kdb_printf(" thread_info=0x%p\n", task_thread_info(tp)); + kdb_printf(" ti flags=0x%lx\n", (unsigned long)task_thread_info(tp)->flags); + +#ifdef CONFIG_NUMA + kdb_printf( + " mempolicy=0x%p il_next=%d\n", + tp->mempolicy, tp->il_next); +#endif + +out: + if (tp) + kfree(tp); + return e; +} + +static int +kdbm_sigset(int argc, const char **argv) +{ + sigset_t *sp = NULL; + unsigned long addr; + long offset=0; + int nextarg; + int e = 0; + int i; + char fmt[32]; + + if (argc != 1) + return KDB_ARGCOUNT; + +#ifndef _NSIG_WORDS + kdb_printf("unavailable on this platform, _NSIG_WORDS not defined.\n"); +#else + nextarg = 1; + if ((e = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) != 0) + return(e); + + if (!(sp = kmalloc(sizeof(*sp), GFP_ATOMIC))) { + kdb_printf("%s: cannot kmalloc sp\n", __FUNCTION__); + goto out; + } + if ((e = kdb_getarea(*sp, addr))) { + kdb_printf("%s: invalid sigset address\n", __FUNCTION__); + goto out; + } + + sprintf(fmt, "[%%d]=0x%%0%dlx ", (int)sizeof(sp->sig[0])*2); + kdb_printf("sigset at 0x%p : ", sp); + for (i=_NSIG_WORDS-1; i >= 0; i--) { + if (i == 0 || sp->sig[i]) { + kdb_printf(fmt, i, sp->sig[i]); + } + } + kdb_printf("\n"); +#endif /* _NSIG_WORDS */ + +out: + if (sp) + kfree(sp); + return e; +} + +static int __init kdbm_task_init(void) +{ + kdb_register("task", kdbm_task, "", "Display task_struct", 0); + kdb_register("sigset", kdbm_sigset, "", "Display sigset_t", 0); + + return 0; +} + +static void __exit kdbm_task_exit(void) +{ + kdb_unregister("task"); + kdb_unregister("sigset"); +} + +module_init(kdbm_task_init) +module_exit(kdbm_task_exit) --- /dev/null +++ b/kdb/modules/kdbm_vm.c @@ -0,0 +1,1041 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 1999-2006 Silicon Graphics, Inc. All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_AUTHOR("SGI"); +MODULE_DESCRIPTION("Debug VM information"); +MODULE_LICENSE("GPL"); + +struct __vmflags { + unsigned long mask; + char *name; +}; + +static struct __vmflags vmflags[] = { + { VM_READ, "VM_READ " }, + { VM_WRITE, "VM_WRITE " }, + { VM_EXEC, "VM_EXEC " }, + { VM_SHARED, "VM_SHARED " }, + { VM_MAYREAD, "VM_MAYREAD " }, + { VM_MAYWRITE, "VM_MAYWRITE " }, + { VM_MAYEXEC, "VM_MAYEXEC " }, + { VM_MAYSHARE, "VM_MAYSHARE " }, + { VM_GROWSDOWN, "VM_GROWSDOWN " }, + { VM_GROWSUP, "VM_GROWSUP " }, + { VM_PFNMAP, "VM_PFNMAP " }, + { VM_DENYWRITE, "VM_DENYWRITE " }, + { VM_EXECUTABLE, "VM_EXECUTABLE " }, + { VM_LOCKED, "VM_LOCKED " }, + { VM_IO, "VM_IO " }, + { VM_SEQ_READ, "VM_SEQ_READ " }, + { VM_RAND_READ, "VM_RAND_READ " }, + { VM_DONTCOPY, "VM_DONTCOPY " }, + { VM_DONTEXPAND, "VM_DONTEXPAND " }, + { VM_RESERVED, "VM_RESERVED " }, + { VM_ACCOUNT, "VM_ACCOUNT " }, + { VM_HUGETLB, "VM_HUGETLB " }, + { VM_NONLINEAR, "VM_NONLINEAR " }, + { VM_MAPPED_COPY, "VM_MAPPED_COPY " }, + { VM_INSERTPAGE, "VM_INSERTPAGE " }, + { 0, "" } +}; + +static int +kdbm_print_vm(struct vm_area_struct *vp, unsigned long addr, int verbose_flg) +{ + struct __vmflags *tp; + + kdb_printf("struct vm_area_struct at 0x%lx for %d bytes\n", + addr, (int) sizeof (struct vm_area_struct)); + + kdb_printf("vm_start = 0x%p vm_end = 0x%p\n", (void *) vp->vm_start, + (void *) vp->vm_end); + kdb_printf("vm_page_prot = 0x%llx\n", + (unsigned long long)pgprot_val(vp->vm_page_prot)); + + kdb_printf("vm_flags: "); + for (tp = vmflags; tp->mask; tp++) { + if (vp->vm_flags & tp->mask) { + kdb_printf(" %s", tp->name); + } + } + kdb_printf("\n"); + + if (!verbose_flg) + return 0; + + kdb_printf("vm_mm = 0x%p\n", (void *) vp->vm_mm); + kdb_printf("vm_next = 0x%p\n", (void *) vp->vm_next); + kdb_printf("shared.vm_set.list.next = 0x%p\n", (void *) vp->shared.vm_set.list.next); + kdb_printf("shared.vm_set.list.prev = 0x%p\n", (void *) vp->shared.vm_set.list.prev); + kdb_printf("shared.vm_set.parent = 0x%p\n", (void *) vp->shared.vm_set.parent); + kdb_printf("shared.vm_set.head = 0x%p\n", (void *) vp->shared.vm_set.head); + kdb_printf("anon_vma_node.next = 0x%p\n", (void *) vp->anon_vma_node.next); + kdb_printf("anon_vma_node.prev = 0x%p\n", (void *) vp->anon_vma_node.prev); + kdb_printf("vm_ops = 0x%p\n", (void *) vp->vm_ops); + if (vp->vm_ops != NULL) { + kdb_printf("vm_ops->open = 0x%p\n", vp->vm_ops->open); + kdb_printf("vm_ops->close = 0x%p\n", vp->vm_ops->close); + kdb_printf("vm_ops->fault = 0x%p\n", vp->vm_ops->fault); +#ifdef HAVE_VMOP_MPROTECT + kdb_printf("vm_ops->mprotect = 0x%p\n", vp->vm_ops->mprotect); +#endif +#ifdef CONFIG_NUMA + kdb_printf("vm_ops->set_policy = 0x%p\n", vp->vm_ops->set_policy); + kdb_printf("vm_ops->get_policy = 0x%p\n", vp->vm_ops->get_policy); +#endif + } + kdb_printf("vm_pgoff = 0x%lx\n", vp->vm_pgoff); + kdb_printf("vm_file = 0x%p\n", (void *) vp->vm_file); + kdb_printf("vm_private_data = 0x%p\n", vp->vm_private_data); +#ifdef CONFIG_NUMA + kdb_printf("vm_policy = 0x%p\n", vp->vm_policy); +#endif + + return 0; +} + +static int +kdbm_print_vmp(struct vm_area_struct *vp, int verbose_flg) +{ + struct __vmflags *tp; + + if (verbose_flg) { + kdb_printf("0x%lx: ", (unsigned long) vp); + } + + kdb_printf("0x%p 0x%p ", (void *) vp->vm_start, (void *) vp->vm_end); + + for (tp = vmflags; tp->mask; tp++) { + if (vp->vm_flags & tp->mask) { + kdb_printf(" %s", tp->name); + } + } + kdb_printf("\n"); + + return 0; +} + + +#ifdef CONFIG_NUMA +#include + +/* + * kdbm_mpol + * + * This function implements the 'mempolicy' command. + * Print a struct mempolicy. + * + * mempolicy
Print struct mempolicy at
+ */ +static int +kdbm_mpol(int argc, const char **argv) +{ + unsigned long addr; + long offset = 0; + int nextarg; + int err = 0; + struct mempolicy *mp = NULL; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + if ((err = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, + NULL)) != 0) + return(err); + + if (!(mp = kmalloc(sizeof(*mp), GFP_ATOMIC))) { + kdb_printf("%s: cannot kmalloc mp\n", __FUNCTION__); + goto out; + } + + if ((err = kdb_getarea(*mp, addr))) { + kdb_printf("%s: invalid mempolicy address\n", __FUNCTION__); + goto out; + } + + kdb_printf("struct mempolicy at 0x%p\n", (struct mempolicy *)addr); + kdb_printf(" refcnt %d\n", atomic_read(&mp->refcnt)); + + switch (mp->mode) { + case MPOL_DEFAULT: + kdb_printf(" mode %d (MPOL_DEFAULT)\n", mp->mode); + break; + + case MPOL_PREFERRED: + kdb_printf(" mode %d (MPOL_PREFERRED)\n", mp->mode); + if (mp->flags & MPOL_F_LOCAL) + kdb_printf(" preferred_node local\n"); + else + kdb_printf(" preferred_node %d\n", mp->v.preferred_node); + break; + + case MPOL_BIND: + case MPOL_INTERLEAVE: + { + int i, nlongs; + unsigned long *longp; + + kdb_printf(" mode %d (%s)\n", mp->mode, + mp->mode == MPOL_INTERLEAVE + ? "MPOL_INTERLEAVE" + : "MPOL_BIND"); + nlongs = (int)BITS_TO_LONGS(MAX_NUMNODES); + kdb_printf(" nodes:"); + longp = mp->v.nodes.bits; + for (i = 0; i < nlongs; i++, longp++) + kdb_printf(" 0x%lx ", *longp); + kdb_printf("\n"); + break; + } + + default: + kdb_printf(" mode %d (unknown)\n", mp->mode); + break; + } +out: + if (mp) + kfree(mp); + return err; +} + +#endif /* CONFIG_NUMA */ + +/* + * kdbm_pgdat + * + * This function implements the 'pgdat' command. + * Print a struct pglist_data (pg_dat_t). + * + * pgdat Print struct pglist_data for node . + * + * Print pglist_data for node 0 if node_id not specified, + * or print the one pglist_data structure if !CONFIG_NUMA. + */ +static int +kdbm_pgdat(int argc, const char **argv) +{ + int err = 0, node_id = 0, i; + pg_data_t *pgdatp = NULL; + +#ifdef CONFIG_NUMA + if (argc > 1) + return KDB_ARGCOUNT; + if (argc == 1) { + int nextarg; + long offset = 0; + unsigned long node_id_ul; + + nextarg = 1; + if ((err = kdbgetaddrarg(argc, argv, &nextarg, &node_id_ul, + &offset, NULL)) != 0) { + return(err); + } + node_id = (int)node_id_ul; + } +#endif + for_each_online_pgdat(pgdatp) { + if (pgdatp->node_id == node_id) + break; + } + if (!pgdatp) { + kdb_printf("%s: specified node not found\n", __FUNCTION__); + return 0; + } + kdb_printf("struct pglist_data at 0x%p node_id = %d\n", + pgdatp, pgdatp->node_id); + + for (i = 0; i < MAX_ZONELISTS; i++) { + int zr; + struct zoneref *zonerefp; + struct zone *zonep; + + zonerefp = pgdatp->node_zonelists[i]._zonerefs; + kdb_printf(" _zonerefs[%d] at 0x%p\n", i, zonerefp); + + for (zr = 0; zr <= MAX_ZONES_PER_ZONELIST; zr++, zonerefp++) { + int z; + pg_data_t *tmp_pgdatp; + + zonep = zonelist_zone(zonerefp); + if (!zonep) + break; + + kdb_printf(" 0x%p", zonep); + + for_each_online_pgdat(tmp_pgdatp) { + for (z = 0; z < MAX_NR_ZONES; z++) { + if (zonep == &tmp_pgdatp->node_zones[z]) { + kdb_printf (" (node %d node_zones[%d])", + tmp_pgdatp->node_id, z); + break; + } + } + if (z != MAX_NR_ZONES) + break; /* found it */ + } + kdb_printf("\n"); + } + } + + kdb_printf(" nr_zones = %d", pgdatp->nr_zones); +#ifdef CONFIG_FLAT_NODE_MEM_MAP + kdb_printf(" node_mem_map = 0x%p\n", pgdatp->node_mem_map); +#endif + kdb_printf(" bdata = 0x%p", pgdatp->bdata); + kdb_printf(" node_start_pfn = 0x%lx\n", pgdatp->node_start_pfn); + kdb_printf(" node_present_pages = %ld (0x%lx)\n", + pgdatp->node_present_pages, pgdatp->node_present_pages); + kdb_printf(" node_spanned_pages = %ld (0x%lx)\n", + pgdatp->node_spanned_pages, pgdatp->node_spanned_pages); + kdb_printf(" kswapd = 0x%p\n", pgdatp->kswapd); + + return err; +} + +/* + * kdbm_vm + * + * This function implements the 'vm' command. Print a vm_area_struct. + * + * vm [-v]
Print vm_area_struct at
+ * vmp [-v] Print all vm_area_structs for + */ + +static int +kdbm_vm(int argc, const char **argv) +{ + unsigned long addr; + long offset = 0; + int nextarg; + int diag; + int verbose_flg = 0; + + if (argc == 2) { + if (strcmp(argv[1], "-v") != 0) { + return KDB_ARGCOUNT; + } + verbose_flg = 1; + } else if (argc != 1) { + return KDB_ARGCOUNT; + } + + if (strcmp(argv[0], "vmp") == 0) { + struct task_struct *g, *tp; + struct vm_area_struct *vp; + pid_t pid; + + if ((diag = kdbgetularg(argv[argc], (unsigned long *) &pid))) + return diag; + + kdb_do_each_thread(g, tp) { + if (tp->pid == pid) { + if (tp->mm != NULL) { + if (verbose_flg) + kdb_printf + ("vm_area_struct "); + kdb_printf + ("vm_start vm_end vm_flags\n"); + vp = tp->mm->mmap; + while (vp != NULL) { + kdbm_print_vmp(vp, verbose_flg); + vp = vp->vm_next; + } + } + return 0; + } + } kdb_while_each_thread(g, tp); + + kdb_printf("No process with pid == %d found\n", pid); + + } else { + struct vm_area_struct v; + + nextarg = argc; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, + NULL)) + || (diag = kdb_getarea(v, addr))) + return (diag); + + kdbm_print_vm(&v, addr, verbose_flg); + } + + return 0; +} + +static int +kdbm_print_pte(pte_t * pte) +{ + kdb_printf("0x%lx (", (unsigned long) pte_val(*pte)); + + if (pte_present(*pte)) { +#ifdef pte_exec + if (pte_exec(*pte)) + kdb_printf("X"); +#endif + if (pte_write(*pte)) + kdb_printf("W"); +#ifdef pte_read + if (pte_read(*pte)) + kdb_printf("R"); +#endif + if (pte_young(*pte)) + kdb_printf("A"); + if (pte_dirty(*pte)) + kdb_printf("D"); + + } else { + kdb_printf("OFFSET=0x%lx ", swp_offset(pte_to_swp_entry(*pte))); + kdb_printf("TYPE=0x%ulx", swp_type(pte_to_swp_entry(*pte))); + } + + kdb_printf(")"); + + /* final newline is output by caller of kdbm_print_pte() */ + + return 0; +} + +/* + * kdbm_pte + * + * This function implements the 'pte' command. Print all pte_t structures + * that map to the given virtual address range (
through
+ * plus ) for the given process. The default value for nbytes is + * one. + * + * pte -m
[] Print all pte_t structures for + * virtual
in address space + * of which is a pointer to a + * mm_struct + * pte -p
[] Print all pte_t structures for + * virtual
in address space + * of + */ + +static int +kdbm_pte(int argc, const char **argv) +{ + unsigned long addr; + long offset = 0; + int nextarg; + unsigned long nbytes = 1; + long npgs; + int diag; + int found; + pid_t pid; + struct task_struct *tp; + struct mm_struct *mm, copy_of_mm; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + if (argc < 3 || argc > 4) { + return KDB_ARGCOUNT; + } + + if (strcmp(argv[1], "-p") == 0) { + if ((diag = kdbgetularg(argv[2], (unsigned long *) &pid))) { + return diag; + } + + found = 0; + for_each_process(tp) { + if (tp->pid == pid) { + if (tp->mm != NULL) { + found = 1; + break; + } + kdb_printf("task structure's mm field is NULL\n"); + return 0; + } + } + + if (!found) { + kdb_printf("No process with pid == %d found\n", pid); + return 0; + } + mm = tp->mm; + } else if (strcmp(argv[1], "-m") == 0) { + + + nextarg = 2; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, + NULL)) + || (diag = kdb_getarea(copy_of_mm, addr))) + return (diag); + mm = ©_of_mm; + } else { + return KDB_ARGCOUNT; + } + + if ((diag = kdbgetularg(argv[3], &addr))) { + return diag; + } + + if (argc == 4) { + if ((diag = kdbgetularg(argv[4], &nbytes))) { + return diag; + } + } + + kdb_printf("vaddr pte\n"); + + npgs = ((((addr & ~PAGE_MASK) + nbytes) + ~PAGE_MASK) >> PAGE_SHIFT); + while (npgs-- > 0) { + + kdb_printf("0x%p ", (void *) (addr & PAGE_MASK)); + + pgd = pgd_offset(mm, addr); + if (pgd_present(*pgd)) { + pud = pud_offset(pgd, addr); + if (pud_present(*pud)) { + pmd = pmd_offset(pud, addr); + if (pmd_present(*pmd)) { + pte = pte_offset_map(pmd, addr); + if (pte_present(*pte)) { + kdbm_print_pte(pte); + } + } + } + } + + kdb_printf("\n"); + addr += PAGE_SIZE; + } + + return 0; +} + +/* + * kdbm_rpte + * + * This function implements the 'rpte' command. Print all pte_t structures + * that contain the given physical page range ( through + * plus ) for the given process. The default value for npages is + * one. + * + * rpte -m [] Print all pte_t structures for + * physical page in address space + * of which is a pointer to a + * mm_struct + * rpte -p [] Print all pte_t structures for + * physical page in address space + * of + */ + +static int +kdbm_rpte(int argc, const char **argv) +{ + unsigned long addr; + unsigned long pfn; + long offset = 0; + int nextarg; + unsigned long npages = 1; + int diag; + int found; + pid_t pid; + struct task_struct *tp; + struct mm_struct *mm, copy_of_mm; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + unsigned long g, u, m, t; + + if (argc < 3 || argc > 4) { + return KDB_ARGCOUNT; + } + + if (strcmp(argv[1], "-p") == 0) { + if ((diag = kdbgetularg(argv[2], (unsigned long *) &pid))) { + return diag; + } + + found = 0; + for_each_process(tp) { + if (tp->pid == pid) { + if (tp->mm != NULL) { + found = 1; + break; + } + kdb_printf("task structure's mm field is NULL\n"); + return 0; + } + } + + if (!found) { + kdb_printf("No process with pid == %d found\n", pid); + return 0; + } + mm = tp->mm; + } else if (strcmp(argv[1], "-m") == 0) { + + + nextarg = 2; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, + NULL)) + || (diag = kdb_getarea(copy_of_mm, addr))) + return (diag); + mm = ©_of_mm; + } else { + return KDB_ARGCOUNT; + } + + if ((diag = kdbgetularg(argv[3], &pfn))) { + return diag; + } + + if (argc == 4) { + if ((diag = kdbgetularg(argv[4], &npages))) { + return diag; + } + } + + /* spaces after vaddr depends on sizeof(unsigned long) */ + kdb_printf("pfn vaddr%*s pte\n", + (int)(2*sizeof(unsigned long) + 2 - 5), " "); + + for (g = 0, pgd = pgd_offset(mm, 0UL); g < PTRS_PER_PGD; ++g, ++pgd) { + if (pgd_none(*pgd) || pgd_bad(*pgd)) + continue; + for (u = 0, pud = pud_offset(pgd, 0UL); u < PTRS_PER_PUD; ++u, ++pud) { + if (pud_none(*pud) || pud_bad(*pud)) + continue; + for (m = 0, pmd = pmd_offset(pud, 0UL); m < PTRS_PER_PMD; ++m, ++pmd) { + if (pmd_none(*pmd) || pmd_bad(*pmd)) + continue; + for (t = 0, pte = pte_offset_map(pmd, 0UL); t < PTRS_PER_PTE; ++t, ++pte) { + if (pte_none(*pte)) + continue; + if (pte_pfn(*pte) < pfn || pte_pfn(*pte) >= (pfn + npages)) + continue; + addr = g << PGDIR_SHIFT; +#ifdef __ia64__ + /* IA64 plays tricks with the pgd mapping to save space. + * This reverses pgd_index(). + */ + { + unsigned long region = g >> (PAGE_SHIFT - 6); + unsigned long l1index = g - (region << (PAGE_SHIFT - 6)); + addr = (region << 61) + (l1index << PGDIR_SHIFT); + } +#endif + addr += (m << PMD_SHIFT) + (t << PAGE_SHIFT); + kdb_printf("0x%-14lx " kdb_bfd_vma_fmt0 " ", + pte_pfn(*pte), addr); + kdbm_print_pte(pte); + kdb_printf("\n"); + } + } + } + } + + return 0; +} + +static int +kdbm_print_dentry(unsigned long daddr) +{ + struct dentry d; + int diag; + char buf[256]; + + kdb_printf("Dentry at 0x%lx\n", daddr); + if ((diag = kdb_getarea(d, (unsigned long)daddr))) + return diag; + + if ((d.d_name.len > sizeof(buf)) || (diag = kdb_getarea_size(buf, (unsigned long)(d.d_name.name), d.d_name.len))) + kdb_printf(" d_name.len = %d d_name.name = 0x%p\n", + d.d_name.len, d.d_name.name); + else + kdb_printf(" d_name.len = %d d_name.name = 0x%p <%.*s>\n", + d.d_name.len, d.d_name.name, + (int)(d.d_name.len), d.d_name.name); + + kdb_printf(" d_count = %d d_flags = 0x%x d_inode = 0x%p\n", + atomic_read(&d.d_count), d.d_flags, d.d_inode); + + kdb_printf(" d_parent = 0x%p\n", d.d_parent); + + kdb_printf(" d_hash.nxt = 0x%p d_hash.prv = 0x%p\n", + d.d_hash.next, d.d_hash.pprev); + + kdb_printf(" d_lru.nxt = 0x%p d_lru.prv = 0x%p\n", + d.d_lru.next, d.d_lru.prev); + + kdb_printf(" d_child.nxt = 0x%p d_child.prv = 0x%p\n", + d.d_u.d_child.next, d.d_u.d_child.prev); + + kdb_printf(" d_subdirs.nxt = 0x%p d_subdirs.prv = 0x%p\n", + d.d_subdirs.next, d.d_subdirs.prev); + + kdb_printf(" d_alias.nxt = 0x%p d_alias.prv = 0x%p\n", + d.d_alias.next, d.d_alias.prev); + + kdb_printf(" d_op = 0x%p d_sb = 0x%p d_fsdata = 0x%p\n", + d.d_op, d.d_sb, d.d_fsdata); + + kdb_printf(" d_iname = %s\n", + d.d_iname); + + if (d.d_inode) { + struct inode i; + kdb_printf("\nInode Entry at 0x%p\n", d.d_inode); + if ((diag = kdb_getarea(i, (unsigned long)d.d_inode))) + return diag; + kdb_printf(" i_mode = 0%o i_nlink = %d i_rdev = 0x%x\n", + i.i_mode, i.i_nlink, i.i_rdev); + + kdb_printf(" i_ino = %ld i_count = %d\n", + i.i_ino, atomic_read(&i.i_count)); + + kdb_printf(" i_hash.nxt = 0x%p i_hash.prv = 0x%p\n", + i.i_hash.next, i.i_hash.pprev); + + kdb_printf(" i_list.nxt = 0x%p i_list.prv = 0x%p\n", + i.i_list.next, i.i_list.prev); + + kdb_printf(" i_dentry.nxt = 0x%p i_dentry.prv = 0x%p\n", + i.i_dentry.next, i.i_dentry.prev); + + } + kdb_printf("\n"); + return 0; +} + +static int +kdbm_filp(int argc, const char **argv) +{ + struct file f; + int nextarg; + unsigned long addr; + long offset; + int diag; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) || + (diag = kdb_getarea(f, addr))) + return diag; + + kdb_printf("File Pointer at 0x%lx\n", addr); + + kdb_printf(" fu_list.nxt = 0x%p fu_list.prv = 0x%p\n", + f.f_u.fu_list.next, f.f_u.fu_list.prev); + + kdb_printf(" f_dentry = 0x%p f_vfsmnt = 0x%p f_op = 0x%p\n", + f.f_dentry, f.f_vfsmnt, f.f_op); + + kdb_printf(" f_count = " kdb_f_count_fmt + " f_flags = 0x%x f_mode = 0x%x\n", + atomic_long_read(&f.f_count), f.f_flags, f.f_mode); + + kdb_printf(" f_pos = %Ld\n", f.f_pos); +#ifdef CONFIG_SECURITY + kdb_printf(" security = 0x%p\n", f.f_security); +#endif + + kdb_printf(" private_data = 0x%p f_mapping = 0x%p\n\n", + f.private_data, f.f_mapping); + + return kdbm_print_dentry((unsigned long)f.f_dentry); +} + +static int +kdbm_fl(int argc, const char **argv) +{ + struct file_lock fl; + int nextarg; + unsigned long addr; + long offset; + int diag; + + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) || + (diag = kdb_getarea(fl, addr))) + return diag; + + kdb_printf("File_lock at 0x%lx\n", addr); + + kdb_printf(" fl_next = 0x%p fl_link.nxt = 0x%p fl_link.prv = 0x%p\n", + fl.fl_next, fl.fl_link.next, fl.fl_link.prev); + kdb_printf(" fl_block.nxt = 0x%p fl_block.prv = 0x%p\n", + fl.fl_block.next, fl.fl_block.prev); + kdb_printf(" fl_owner = 0x%p fl_pid = %d fl_wait = 0x%p\n", + fl.fl_owner, fl.fl_pid, &fl.fl_wait); + kdb_printf(" fl_file = 0x%p fl_flags = 0x%x\n", + fl.fl_file, fl.fl_flags); + kdb_printf(" fl_type = %d fl_start = 0x%llx fl_end = 0x%llx\n", + fl.fl_type, fl.fl_start, fl.fl_end); + + kdb_printf(" file_lock_operations"); + if (fl.fl_ops) + kdb_printf("\n fl_copy_lock = 0x%p fl_release_private = 0x%p\n", + fl.fl_ops->fl_copy_lock, fl.fl_ops->fl_release_private); + else + kdb_printf(" empty\n"); + + kdb_printf(" lock_manager_operations"); + if (fl.fl_lmops) + kdb_printf("\n fl_compare_owner = 0x%p fl_notify = 0x%p\n", + fl.fl_lmops->fl_compare_owner, fl.fl_lmops->fl_notify); + else + kdb_printf(" empty\n"); + + kdb_printf(" fl_fasync = 0x%p fl_break 0x%lx\n", + fl.fl_fasync, fl.fl_break_time); + + return 0; +} + + +static int +kdbm_dentry(int argc, const char **argv) +{ + int nextarg; + unsigned long addr; + long offset; + int diag; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL))) + return diag; + + return kdbm_print_dentry(addr); +} + +static int +kdbm_kobject(int argc, const char **argv) +{ + struct kobject k; + int nextarg; + unsigned long addr; + long offset; + int diag; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) || + (diag = kdb_getarea(k, addr))) + return diag; + + + kdb_printf("kobject at 0x%lx\n", addr); + + if (k.name) { + char c; + kdb_printf(" name 0x%p", k.name); + if (kdb_getarea(c, (unsigned long)k.name) == 0) + kdb_printf(" '%s'", k.name); + kdb_printf("\n"); + } + + if (k.name != kobject_name((struct kobject *)addr)) + kdb_printf(" name '%.20s'\n", k.name); + + kdb_printf(" kref.refcount %d'\n", atomic_read(&k.kref.refcount)); + + kdb_printf(" entry.next = 0x%p entry.prev = 0x%p\n", + k.entry.next, k.entry.prev); + + kdb_printf(" parent = 0x%p kset = 0x%p ktype = 0x%p sd = 0x%p\n", + k.parent, k.kset, k.ktype, k.sd); + + return 0; +} + +static int +kdbm_sh(int argc, const char **argv) +{ + int diag; + int nextarg; + unsigned long addr; + long offset = 0L; + struct Scsi_Host sh; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL)) || + (diag = kdb_getarea(sh, addr))) + return diag; + + kdb_printf("Scsi_Host at 0x%lx\n", addr); + kdb_printf("host_queue = 0x%p\n", sh.__devices.next); + kdb_printf("ehandler = 0x%p eh_action = 0x%p\n", + sh.ehandler, sh.eh_action); + kdb_printf("host_wait = 0x%p hostt = 0x%p\n", + &sh.host_wait, sh.hostt); + kdb_printf("host_failed = %d host_no = %d resetting = %d\n", + sh.host_failed, sh.host_no, sh.resetting); + kdb_printf("max id/lun/channel = [%d/%d/%d] this_id = %d\n", + sh.max_id, sh.max_lun, sh.max_channel, sh.this_id); + kdb_printf("can_queue = %d cmd_per_lun = %d sg_tablesize = %d u_isa_dma = %d\n", + sh.can_queue, sh.cmd_per_lun, sh.sg_tablesize, sh.unchecked_isa_dma); + kdb_printf("host_blocked = %d reverse_ordering = %d \n", + sh.host_blocked, sh.reverse_ordering); + + return 0; +} + +static int +kdbm_sd(int argc, const char **argv) +{ + int diag; + int nextarg; + unsigned long addr; + long offset = 0L; + struct scsi_device *sd = NULL; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL))) + goto out; + if (!(sd = kmalloc(sizeof(*sd), GFP_ATOMIC))) { + kdb_printf("kdbm_sd: cannot kmalloc sd\n"); + goto out; + } + if ((diag = kdb_getarea(*sd, addr))) + goto out; + + kdb_printf("scsi_device at 0x%lx\n", addr); + kdb_printf("next = 0x%p prev = 0x%p host = 0x%p\n", + sd->siblings.next, sd->siblings.prev, sd->host); + kdb_printf("device_busy = %d current_cmnd 0x%p\n", + sd->device_busy, sd->current_cmnd); + kdb_printf("id/lun/chan = [%d/%d/%d] single_lun = %d device_blocked = %d\n", + sd->id, sd->lun, sd->channel, sd->sdev_target->single_lun, sd->device_blocked); + kdb_printf("queue_depth = %d current_tag = %d scsi_level = %d\n", + sd->queue_depth, sd->current_tag, sd->scsi_level); + kdb_printf("%8.8s %16.16s %4.4s\n", sd->vendor, sd->model, sd->rev); +out: + if (sd) + kfree(sd); + return diag; +} + +static int +kdbm_sc(int argc, const char **argv) +{ + int diag; + int nextarg; + unsigned long addr; + long offset = 0L; + struct scsi_cmnd *sc = NULL; + + if (argc != 1) + return KDB_ARGCOUNT; + + nextarg = 1; + if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL))) + goto out; + if (!(sc = kmalloc(sizeof(*sc), GFP_ATOMIC))) { + kdb_printf("kdbm_sc: cannot kmalloc sc\n"); + goto out; + } + if ((diag = kdb_getarea(*sc, addr))) + goto out; + + kdb_printf("scsi_cmnd at 0x%lx\n", addr); + kdb_printf("device = 0x%p next = 0x%p\n", + sc->device, sc->list.next); + kdb_printf("serial_number = %ld retries = %d\n", + sc->serial_number, sc->retries); + kdb_printf("cmd_len = %d\n", sc->cmd_len); + kdb_printf("cmnd = [%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x/%2.2x]\n", + sc->cmnd[0], sc->cmnd[1], sc->cmnd[2], sc->cmnd[3], sc->cmnd[4], + sc->cmnd[5], sc->cmnd[6], sc->cmnd[7], sc->cmnd[8], sc->cmnd[9], + sc->cmnd[10], sc->cmnd[11]); + kdb_printf("request_buffer = 0x%p request_bufflen = %d\n", + scsi_sglist(sc), scsi_bufflen(sc)); + kdb_printf("use_sg = %d\n", scsi_sg_count(sc)); + kdb_printf("underflow = %d transfersize = %d\n", + sc->underflow, sc->transfersize); + kdb_printf("tag = %d\n", sc->tag); + +out: + if (sc) + kfree(sc); + return diag; +} + +static int __init kdbm_vm_init(void) +{ + kdb_register("vm", kdbm_vm, "[-v] ", "Display vm_area_struct", 0); + kdb_register("vmp", kdbm_vm, "[-v] ", "Display all vm_area_struct for ", 0); +#ifdef CONFIG_NUMA + kdb_register("mempolicy", kdbm_mpol, "", "Display mempolicy structure", 0); + kdb_register("pgdat", kdbm_pgdat, "", "Display pglist_data node structure", 0); +#else + kdb_register("pgdat", kdbm_pgdat, "", "Display pglist_data node structure", 0); +#endif + kdb_register("pte", kdbm_pte, "( -m | -p ) []", "Display pte_t for mm_struct or pid", 0); + kdb_register("rpte", kdbm_rpte, "( -m | -p ) []", "Find pte_t containing pfn for mm_struct or pid", 0); + kdb_register("dentry", kdbm_dentry, "", "Display interesting dentry stuff", 0); + kdb_register("kobject", kdbm_kobject, "", "Display interesting kobject stuff", 0); + kdb_register("filp", kdbm_filp, "", "Display interesting filp stuff", 0); + kdb_register("fl", kdbm_fl, "", "Display interesting file_lock 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; +} + +static void __exit kdbm_vm_exit(void) +{ + kdb_unregister("vm"); + kdb_unregister("vmp"); +#ifdef CONFIG_NUMA + kdb_unregister("mempolicy"); +#endif + kdb_unregister("pgdat"); + kdb_unregister("pte"); + kdb_unregister("rpte"); + kdb_unregister("dentry"); + kdb_unregister("kobject"); + kdb_unregister("filp"); + kdb_unregister("fl"); + kdb_unregister("sh"); + kdb_unregister("sd"); + kdb_unregister("sc"); +} + +module_init(kdbm_vm_init) +module_exit(kdbm_vm_exit) --- /dev/null +++ b/kdb/modules/kdbm_x86.c @@ -0,0 +1,1093 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Author: Vamsi Krishna S. + * (C) 2003 IBM Corporation. + * 2006-10-10 Keith Owens + * Reworked to include x86_64 support + * Copyright (c) 2006 Silicon Graphics, Inc. All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#if 0 +#include +#endif + +MODULE_AUTHOR("Vamsi Krishna S./IBM"); +MODULE_DESCRIPTION("x86 specific information (gdt/idt/ldt/page tables)"); +MODULE_LICENSE("GPL"); + +/* Isolate as many of the i386/x86_64 differences as possible in one spot */ + +#ifdef CONFIG_X86_64 + +#define KDB_X86_64 1 +#define MOVLQ "movq" + +typedef struct desc_struct kdb_desc_t; +typedef struct gate_struct64 kdb_gate_desc_t; + +#define KDB_SYS_DESC_OFFSET(d) ((unsigned long)d->offset_high << 32 | d->offset_middle << 16 | d->offset_low) +#define KDB_SYS_DESC_CALLG_COUNT(d) 0 + +#else /* !CONFIG_X86_64 */ + +#define KDB_X86_64 0 +#define MOVLQ "movl" + +/* i386 has no detailed mapping for the 8 byte segment descriptor, copy the + * x86_64 one and merge the l and avl bits. + */ +struct kdb_desc { + u16 limit0; + u16 base0; + unsigned base1 : 8, type : 4, s : 1, dpl : 2, p : 1; + unsigned limit : 4, avl : 2, d : 1, g : 1, base2 : 8; +} __attribute__((packed)); +typedef struct kdb_desc kdb_desc_t; + +/* i386 has no detailed mapping for the 8 byte gate descriptor, base it on the + * x86_64 one. + */ +struct kdb_gate_desc { + u16 offset_low; + u16 segment; + unsigned res : 8, type : 4, s : 1, dpl : 2, p : 1; + u16 offset_middle; +} __attribute__((packed)); +typedef struct kdb_gate_desc kdb_gate_desc_t; + +#define KDB_SYS_DESC_OFFSET(d) ((unsigned long)(d->offset_middle << 16 | d->offset_low)) +#define KDB_SYS_DESC_CALLG_COUNT(d) ((unsigned int)(d->res & 0x0F)) + +#endif /* CONFIG_X86_64 */ + +#define KDB_SEL_MAX 0x2000 +#define KDB_IDT_MAX 0x100 +#define KDB_SYS_DESC_TYPE_TSS16 0x01 +#define KDB_SYS_DESC_TYPE_LDT 0x02 +#define KDB_SYS_DESC_TYPE_TSSB16 0x03 +#define KDB_SYS_DESC_TYPE_CALLG16 0x04 +#define KDB_SYS_DESC_TYPE_TASKG 0x05 +#define KDB_SYS_DESC_TYPE_INTG16 0x06 +#define KDB_SYS_DESC_TYPE_TRAP16 0x07 + +#define KDB_SYS_DESC_TYPE_TSS 0x09 +#define KDB_SYS_DESC_TYPE_TSSB 0x0b +#define KDB_SYS_DESC_TYPE_CALLG 0x0c +#define KDB_SYS_DESC_TYPE_INTG 0x0e +#define KDB_SYS_DESC_TYPE_TRAPG 0x0f + +#define KDB_SEG_DESC_TYPE_CODE 0x08 +#define KDB_SEG_DESC_TYPE_CODE_R 0x02 +#define KDB_SEG_DESC_TYPE_DATA_W 0x02 +#define KDB_SEG_DESC_TYPE_CODE_C 0x02 /* conforming */ +#define KDB_SEG_DESC_TYPE_DATA_D 0x02 /* expand-down */ +#define KDB_SEG_DESC_TYPE_A 0x01 /* accessed */ + +#define _LIMIT(d) ((unsigned long)((d)->limit << 16 | (d)->limit0)) +#define KDB_SEG_DESC_LIMIT(d) ((d)->g ? ((_LIMIT(d)+1) << 12) -1 : _LIMIT(d)) + +static unsigned long kdb_seg_desc_base(kdb_desc_t *d) +{ + unsigned long base = d->base2 << 24 | d->base1 << 16 | d->base0; +#ifdef CONFIG_X86_64 + switch (d->type) { + case KDB_SYS_DESC_TYPE_TSS: + case KDB_SYS_DESC_TYPE_TSSB: + case KDB_SYS_DESC_TYPE_LDT: + base += (unsigned long)(((struct ldttss_desc64 *)d)->base3) << 32; + break; + } +#endif + return base; +} + +/* helper functions to display system registers in verbose mode */ +static void display_gdtr(void) +{ + struct desc_ptr gdtr; + + __asm__ __volatile__ ("sgdt %0\n\t" : "=m"(gdtr)); + kdb_printf("gdtr.address = " kdb_machreg_fmt0 ", gdtr.size = 0x%x\n", + gdtr.address, gdtr.size); + + return; +} + +static void display_ldtr(void) +{ + struct desc_ptr gdtr; + unsigned long ldtr; + + __asm__ __volatile__ ("sgdt %0\n\t" : "=m"(gdtr)); + __asm__ __volatile__ ("sldt %0\n\t" : "=m"(ldtr)); + ldtr &= 0xfff8; /* extract the index */ + + kdb_printf("ldtr = " kdb_machreg_fmt0 " ", ldtr); + + if (ldtr < gdtr.size) { + kdb_desc_t *ldt_desc = + (kdb_desc_t *)(gdtr.address + ldtr); + kdb_printf("base=" kdb_machreg_fmt0 + ", limit=" kdb_machreg_fmt "\n", + kdb_seg_desc_base(ldt_desc), + KDB_SEG_DESC_LIMIT(ldt_desc)); + } else { + kdb_printf("invalid\n"); + } + + return; +} + +static void display_idtr(void) +{ + struct desc_ptr idtr; + __asm__ __volatile__ ("sidt %0\n\t" : "=m"(idtr)); + kdb_printf("idtr.address = " kdb_machreg_fmt0 ", idtr.size = 0x%x\n", + idtr.address, idtr.size); + return; +} + +static const char *cr0_flags[] = { + "pe", "mp", "em", "ts", "et", "ne", NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "wp", NULL, "am", NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, "nw", "cd", "pg"}; + +static void display_cr0(void) +{ + kdb_machreg_t cr0; + int i; + __asm__ (MOVLQ " %%cr0,%0\n\t":"=r"(cr0)); + kdb_printf("cr0 = " kdb_machreg_fmt0, cr0); + for (i = 0; i < ARRAY_SIZE(cr0_flags); i++) { + if (test_bit(i, &cr0) && cr0_flags[i]) + kdb_printf(" %s", cr0_flags[i]); + } + kdb_printf("\n"); + return; +} + +static void display_cr3(void) +{ + kdb_machreg_t cr3; + __asm__ (MOVLQ " %%cr3,%0\n\t":"=r"(cr3)); + kdb_printf("cr3 = " kdb_machreg_fmt0 " ", cr3); + if (cr3 & 0x08) + kdb_printf("pwt "); + if (cr3 & 0x10) + kdb_printf("pcd "); + kdb_printf("%s=" kdb_machreg_fmt0 "\n", + KDB_X86_64 ? "pml4" : "pgdir", cr3 & PAGE_MASK); + return; +} + +static const char *cr4_flags[] = { + "vme", "pvi", "tsd", "de", + "pse", "pae", "mce", "pge", + "pce", "osfxsr" "osxmmexcpt"}; + +static void display_cr4(void) +{ + kdb_machreg_t cr4; + int i; + __asm__ (MOVLQ " %%cr4,%0\n\t":"=r"(cr4)); + kdb_printf("cr4 = " kdb_machreg_fmt0, cr4); + for (i = 0; i < ARRAY_SIZE(cr4_flags); i++) { + if (test_bit(i, &cr4)) + kdb_printf(" %s", cr4_flags[i]); + } + kdb_printf("\n"); + return; +} + +static void display_cr8(void) +{ +#ifdef CONFIG_X86_64 + kdb_machreg_t cr8; + __asm__ (MOVLQ " %%cr8,%0\n\t":"=r"(cr8)); + kdb_printf("cr8 = " kdb_machreg_fmt0 "\n", cr8); + return; +#endif /* CONFIG_X86_64 */ +} + +static char *dr_type_name[] = { "exec", "write", "io", "rw" }; + +static void display_dr_status(int nr, int enabled, int local, int len, int type) +{ + if (!enabled) { + kdb_printf("\tdebug register %d: not enabled\n", nr); + return; + } + + kdb_printf(" debug register %d: %s, len = %d, type = %s\n", + nr, + local? " local":"global", + len, + dr_type_name[type]); +} + +static void display_dr(void) +{ + kdb_machreg_t dr0, dr1, dr2, dr3, dr6, dr7; + int dbnr, set; + + __asm__ (MOVLQ " %%db0,%0\n\t":"=r"(dr0)); + __asm__ (MOVLQ " %%db1,%0\n\t":"=r"(dr1)); + __asm__ (MOVLQ " %%db2,%0\n\t":"=r"(dr2)); + __asm__ (MOVLQ " %%db3,%0\n\t":"=r"(dr3)); + __asm__ (MOVLQ " %%db6,%0\n\t":"=r"(dr6)); + __asm__ (MOVLQ " %%db7,%0\n\t":"=r"(dr7)); + + kdb_printf("dr0 = " kdb_machreg_fmt0 " dr1 = " kdb_machreg_fmt0 + " dr2 = " kdb_machreg_fmt0 " dr3 = " kdb_machreg_fmt0 "\n", + dr0, dr1, dr2, dr3); + kdb_printf("dr6 = " kdb_machreg_fmt0 " ", dr6); + dbnr = dr6 & DR6_DR_MASK; + if (dbnr) { + int nr; + switch(dbnr) { + case 1: + nr = 0; break; + case 2: + nr = 1; break; + case 4: + nr = 2; break; + default: + nr = 3; break; + } + kdb_printf("debug register hit = %d", nr); + } else if (dr6 & DR_STEP) { + kdb_printf("single step"); + } else if (dr6 & DR_SWITCH) { + kdb_printf("task switch"); + } + kdb_printf("\n"); + + kdb_printf("dr7 = " kdb_machreg_fmt0 "\n", dr7); + set = DR7_L0(dr7) || DR7_G0(dr7); + display_dr_status(0, set, DR7_L0(dr7), DR7_LEN0(dr7), DR7_RW0(dr7)); + set = DR7_L1(dr7) || DR7_G1(dr7); + display_dr_status(1, set, DR7_L1(dr7), DR7_LEN1(dr7), DR7_RW1(dr7)); + set = DR7_L2(dr7) || DR7_G2(dr7); + display_dr_status(2, set, DR7_L2(dr7), DR7_LEN2(dr7), DR7_RW2(dr7)); + set = DR7_L3(dr7) || DR7_G3(dr7); + display_dr_status(3, set, DR7_L3(dr7), DR7_LEN3(dr7), DR7_RW3(dr7)); +} + +static char *set_eflags[] = { + "carry", NULL, "parity", NULL, "adjust", NULL, "zero", "sign", + "trace", "intr-on", "dir", "overflow", NULL, NULL, "nestedtask", NULL, + "resume", "vm", "align", "vif", "vip", "id"}; + +static void display_eflags(unsigned long ef) +{ + int i, iopl; + kdb_printf("eflags = " kdb_machreg_fmt0 " ", ef); + for (i = 0; i < ARRAY_SIZE(set_eflags); i++) { + if (test_bit(i, &ef) && set_eflags[i]) + kdb_printf("%s ", set_eflags[i]); + } + + iopl = (ef & 0x00003000) >> 12; + kdb_printf("iopl=%d\n", iopl); + return; +} + +static void display_tss(struct tss_struct *t) +{ +#ifdef CONFIG_X86_64 + int i; + kdb_printf(" sp0 = 0x%016Lx, sp1 = 0x%016Lx\n", + t->x86_tss.sp0, t->x86_tss.sp1); + kdb_printf(" sp2 = 0x%016Lx\n", t->x86_tss.sp2); + for (i = 0; i < ARRAY_SIZE(t->x86_tss.ist); ++i) + kdb_printf(" ist[%d] = 0x%016Lx\n", + i, t->x86_tss.ist[i]); + kdb_printf(" iomap = 0x%04x\n", t->x86_tss.io_bitmap_base); +#else /* !CONFIG_X86_64 */ + kdb_printf(" cs = %04x, ip = " kdb_machreg_fmt0 "\n", + t->x86_tss.es, t->x86_tss.ip); + kdb_printf(" ss = %04x, sp = " kdb_machreg_fmt0 "\n", + t->x86_tss.ss, t->x86_tss.sp); + kdb_printf(" ss0 = %04x, sp0 = " kdb_machreg_fmt0 "\n", + t->x86_tss.ss0, t->x86_tss.sp0); + kdb_printf(" ss1 = %04x, sp1 = " kdb_machreg_fmt0 "\n", + t->x86_tss.ss1, t->x86_tss.sp1); + kdb_printf(" ss2 = %04x, sp2 = " kdb_machreg_fmt0 "\n", + t->x86_tss.ss2, t->x86_tss.sp2); + kdb_printf(" ldt = %04x, cr3 = " kdb_machreg_fmt0 "\n", + t->x86_tss.ldt, t->x86_tss.__cr3); + kdb_printf(" ds = %04x, es = %04x fs = %04x gs = %04x\n", + t->x86_tss.ds, t->x86_tss.es, t->x86_tss.fs, t->x86_tss.gs); + kdb_printf(" ax = " kdb_machreg_fmt0 ", bx = " kdb_machreg_fmt0 + " cx = " kdb_machreg_fmt0 " dx = " kdb_machreg_fmt0 "\n", + t->x86_tss.ax, t->x86_tss.bx, t->x86_tss.cx, t->x86_tss.dx); + kdb_printf(" si = " kdb_machreg_fmt0 ", di = " kdb_machreg_fmt0 + " bp = " kdb_machreg_fmt0 "\n", + t->x86_tss.si, t->x86_tss.di, t->x86_tss.bp); + kdb_printf(" trace = %d, iomap = 0x%04x\n", t->x86_tss.trace, t->x86_tss.io_bitmap_base); +#endif /* CONFIG_X86_64 */ +} + +static char *gate_desc_types[] = { +#ifdef CONFIG_X86_64 + "reserved-0", "reserved-1", "ldt", "reserved-3", + "reserved-4", "reserved-5", "reserved-6", "reserved-7", + "reserved-8", "tss-avlb", "reserved-10", "tss-busy", + "callgate", "reserved-13", "intgate", "trapgate", +#else /* CONFIG_X86_64 */ + "reserved-0", "tss16-avlb", "ldt", "tss16-busy", + "callgate16", "taskgate", "intgate16", "trapgate16", + "reserved-8", "tss-avlb", "reserved-10", "tss-busy", + "callgate", "reserved-13", "intgate", "trapgate", +#endif /* CONFIG_X86_64 */ +}; + +static void +display_gate_desc(kdb_gate_desc_t *d) +{ + kdb_printf("%-11s ", gate_desc_types[d->type]); + + switch(d->type) { + case KDB_SYS_DESC_TYPE_LDT: + kdb_printf("base="); + kdb_symbol_print(kdb_seg_desc_base((kdb_desc_t *)d), NULL, + KDB_SP_DEFAULT); + kdb_printf(" limit=" kdb_machreg_fmt " dpl=%d\n", + KDB_SEG_DESC_LIMIT((kdb_desc_t *)d), d->dpl); + break; + case KDB_SYS_DESC_TYPE_TSS: + case KDB_SYS_DESC_TYPE_TSS16: + case KDB_SYS_DESC_TYPE_TSSB: + case KDB_SYS_DESC_TYPE_TSSB16: + { + struct tss_struct *tss = + (struct tss_struct *) + kdb_seg_desc_base((kdb_desc_t *)d); + kdb_printf("base="); + kdb_symbol_print((unsigned long)tss, NULL, KDB_SP_DEFAULT); + kdb_printf(" limit=" kdb_machreg_fmt " dpl=%d\n", + KDB_SEG_DESC_LIMIT((kdb_desc_t *)d), d->dpl); + display_tss(tss); + break; + } + case KDB_SYS_DESC_TYPE_CALLG16: + kdb_printf("segment=0x%4.4x off=", d->segment); + kdb_symbol_print(KDB_SYS_DESC_OFFSET(d), NULL, KDB_SP_DEFAULT); + kdb_printf(" dpl=%d wc=%d\n", + d->dpl, KDB_SYS_DESC_CALLG_COUNT(d)); + break; + case KDB_SYS_DESC_TYPE_CALLG: + kdb_printf("segment=0x%4.4x off=", d->segment); + kdb_symbol_print(KDB_SYS_DESC_OFFSET(d), NULL, KDB_SP_DEFAULT); + kdb_printf(" dpl=%d\n", d->dpl); + break; + default: + kdb_printf("segment=0x%4.4x off=", d->segment); + if (KDB_SYS_DESC_OFFSET(d)) + kdb_symbol_print(KDB_SYS_DESC_OFFSET(d), NULL, + KDB_SP_DEFAULT); + else + kdb_printf(kdb_machreg_fmt0, KDB_SYS_DESC_OFFSET(d)); + kdb_printf(" dpl=%d", d->dpl); +#ifdef CONFIG_X86_64 + if (d->ist) + kdb_printf(" ist=%d", d->ist); +#endif /* CONFIG_X86_64 */ + kdb_printf("\n"); + break; + } +} + +static void +display_seg_desc(kdb_desc_t *d) +{ + unsigned char type = d->type; + + if (type & KDB_SEG_DESC_TYPE_CODE) { + kdb_printf("%-11s base=" kdb_machreg_fmt0 " limit=" + kdb_machreg_fmt " dpl=%d %c%c%c %s %s %s \n", + "code", + kdb_seg_desc_base(d), KDB_SEG_DESC_LIMIT(d), + d->dpl, + (type & KDB_SEG_DESC_TYPE_CODE_R)?'r':'-', + '-', 'x', +#ifdef CONFIG_X86_64 + d->l ? "64b" : d->d ? "32b" : "16b", +#else /* !CONFIG_X86_64 */ + d->d ? "32b" : "16b", +#endif /* CONFIG_X86_64 */ + (type & KDB_SEG_DESC_TYPE_A)?"ac":"", + (type & KDB_SEG_DESC_TYPE_CODE_C)?"conf":""); + } else { + kdb_printf("%-11s base=" kdb_machreg_fmt0 " limit=" + kdb_machreg_fmt " dpl=%d %c%c%c %s %s %s \n", + "data", + kdb_seg_desc_base(d), KDB_SEG_DESC_LIMIT(d), + d->dpl, + 'r', + (type & KDB_SEG_DESC_TYPE_DATA_W)?'w':'-', + '-', + d->d ? "32b" : "16b", + (type & KDB_SEG_DESC_TYPE_A)?"ac":"", + (type & KDB_SEG_DESC_TYPE_DATA_D)?"down":""); + } +} + +static int +kdb_parse_two_numbers(int argc, const char **argv, int *sel, int *count, + int *last_sel, int *last_count) +{ + int diag; + + if (argc > 2) + return KDB_ARGCOUNT; + + kdbgetintenv("MDCOUNT", count); + + if (argc == 0) { + *sel = *last_sel; + if (*last_count) + *count = *last_count; + } else { + unsigned long val; + + if (argc >= 1) { + diag = kdbgetularg(argv[1], &val); + if (diag) + return diag; + *sel = val; + } + if (argc >= 2) { + diag = kdbgetularg(argv[2], &val); + if (diag) + return diag; + *count = (int) val; + *last_count = (int) val; + } else if (*last_count) { + *count = *last_count; + } + } + return 0; +} + +/* + * kdb_gdt + * + * This function implements the 'gdt' command. + * + * gdt [ []] + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ +static int +kdb_gdt(int argc, const char **argv) +{ + int sel = 0; + struct desc_ptr gdtr; + int diag, count = 8; + kdb_desc_t *gdt; + unsigned int max_sel; + static int last_sel = 0, last_count = 0; + + diag = kdb_parse_two_numbers(argc, argv, &sel, &count, + &last_sel, &last_count); + if (diag) + return diag; + + __asm__ __volatile__ ("sgdt %0\n\t" : "=m"(gdtr)); + gdt = (kdb_desc_t *) gdtr.address; + + max_sel = (gdtr.size + 1) / sizeof(kdb_desc_t); + if (sel >= max_sel) { + kdb_printf("Maximum selector (%d) reached\n", max_sel); + return 0; + } + + if (sel + count > max_sel) + count = max_sel - sel; + + while (count--) { + kdb_desc_t *d = &gdt[sel]; + kdb_printf("0x%4.4x ", sel++); + + if (!d->p) { + kdb_printf("not present\n"); + continue; + } + if (d->s) { + display_seg_desc(d); + } else { + display_gate_desc((kdb_gate_desc_t *)d); + if (KDB_X86_64 && count) { + ++sel; /* this descriptor occupies two slots */ + --count; + } + } + } + + last_sel = sel; + return 0; +} + +/* + * kdb_ldt + * + * This function implements the 'ldt' command. + * + * ldt [ []] + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ +static int +kdb_ldt(int argc, const char **argv) +{ + int sel = 0; + struct desc_ptr gdtr; + unsigned long ldtr = 0; + int diag, count = 8; + kdb_desc_t *ldt, *ldt_desc; + unsigned int max_sel; + static int last_sel = 0, last_count = 0; + + diag = kdb_parse_two_numbers(argc, argv, &sel, &count, + &last_sel, &last_count); + if (diag) + return diag; + + if (strcmp(argv[0], "ldtp") == 0) { + kdb_printf("pid=%d, process=%s\n", + kdb_current_task->pid, kdb_current_task->comm); + if (!kdb_current_task->mm || + !kdb_current_task->mm->context.ldt) { + kdb_printf("no special LDT for this process\n"); + return 0; + } + ldt = kdb_current_task->mm->context.ldt; + max_sel = kdb_current_task->mm->context.size; + } else { + + /* sldt gives the GDT selector for the segment containing LDT */ + __asm__ __volatile__ ("sgdt %0\n\t" : "=m"(gdtr)); + __asm__ __volatile__ ("sldt %0\n\t" : "=m"(ldtr)); + ldtr &= 0xfff8; /* extract the index */ + + if (ldtr > gdtr.size+1) { + kdb_printf("invalid ldtr\n"); + return 0; + } + + ldt_desc = (kdb_desc_t *)(gdtr.address + ldtr); + ldt = (kdb_desc_t *)kdb_seg_desc_base(ldt_desc); + max_sel = (KDB_SEG_DESC_LIMIT(ldt_desc)+1) / sizeof(kdb_desc_t); + } + + if (sel >= max_sel) { + kdb_printf("Maximum selector (%d) reached\n", max_sel); + return 0; + } + + if (sel + count > max_sel) + count = max_sel - sel; + + while (count--) { + kdb_desc_t *d = &ldt[sel]; + kdb_printf("0x%4.4x ", sel++); + + if (!d->p) { + kdb_printf("not present\n"); + continue; + } + if (d->s) { + display_seg_desc(d); + } else { + display_gate_desc((kdb_gate_desc_t *)d); + if (KDB_X86_64 && count) { + ++sel; /* this descriptor occupies two slots */ + --count; + } + } + } + + last_sel = sel; + return 0; +} + +/* + * kdb_idt + * + * This function implements the 'idt' command. + * + * idt [ []] + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ +static int +kdb_idt(int argc, const char **argv) +{ + int vec = 0; + struct desc_ptr idtr; + int diag, count = 8; + kdb_gate_desc_t *idt; + unsigned int max_entries; + static int last_vec = 0, last_count = 0; + + diag = kdb_parse_two_numbers(argc, argv, &vec, &count, + &last_vec, &last_count); + if (diag) + return diag; + + __asm__ __volatile__ ("sidt %0\n\t" : "=m"(idtr)); + idt = (kdb_gate_desc_t *)idtr.address; + + max_entries = (idtr.size+1) / sizeof(kdb_gate_desc_t); + if (vec >= max_entries) { + kdb_printf("Maximum vector (%d) reached\n", max_entries); + return 0; + } + + if (vec + count > max_entries) + count = max_entries - vec; + + while (count--) { + kdb_gate_desc_t *d = &idt[vec]; + kdb_printf("0x%4.4x ", vec++); + if (!d->p) { + kdb_printf("not present\n"); + continue; + } +#ifndef CONFIG_X86_64 + if (d->s) { + kdb_printf("invalid\n"); + continue; + } +#endif /* CONFIG_X86_64 */ + display_gate_desc(d); + } + + last_vec = vec; + + return 0; +} + +#if 0 +static int +get_pagetables(unsigned long addr, pgd_t **pgdir, pmd_t **pgmiddle, pte_t **pte) +{ + pgd_t *d; + pmd_t *m; + pte_t *t; + + if (addr > PAGE_OFFSET) { + d = pgd_offset_k(addr); + } else { + kdb_printf("pid=%d, process=%s\n", kdb_current_task->pid, kdb_current_task->comm); + d = pgd_offset(kdb_current_task->mm, addr); + } + + if (pgd_none(*d) || pgd_bad(*d)) { + *pgdir = NULL; + *pgmiddle = NULL; + *pte = NULL; + return 0; + } else { + *pgdir = d; + } + + /* if _PAGE_PSE is set, pgdir points directly to the page. */ + if (pgd_val(*d) & _PAGE_PSE) { + *pgmiddle = NULL; + *pte = NULL; + return 0; + } + + m = pmd_offset(d, addr); + if (pmd_none(*m) || pmd_bad(*m)) { + *pgmiddle = NULL; + *pte = NULL; + return 0; + } else { + *pgmiddle = m; + } + + t = pte_offset(m, addr); + if (pte_none(*t)) { + *pte = NULL; + return 0; + } else { + *pte = t; + } + kdb_printf("\naddr=%08lx, pgd=%08lx, pmd=%08lx, pte=%08lx\n", + addr, + (unsigned long) pgd_val(*d), + (unsigned long) pmd_val(*m), + (unsigned long) pte_val(*t)); + return 0; +} +#endif + +#define FORMAT_PGDIR(entry) \ + kdb_printf("frame=%05lx %c %s %c %c %c %s %c %s %s \n",\ + (entry >> PAGE_SHIFT), \ + (entry & _PAGE_PRESENT)?'p':'n', \ + (entry & _PAGE_RW)?"rw":"ro", \ + (entry & _PAGE_USER)?'u':'s', \ + (entry & _PAGE_ACCESSED)?'a':' ', \ + ' ', \ + (entry & _PAGE_PSE)?"4M":"4K", \ + (entry & _PAGE_GLOBAL)?'g':' ', \ + (entry & _PAGE_PWT)?"wt":"wb", \ + (entry & _PAGE_PCD)?"cd":" "); + +#define FORMAT_PTE(p, entry) \ + kdb_printf("frame=%05lx %c%c%c %c %c %c %s %c %s %s\n", \ + (entry >> PAGE_SHIFT), \ + (pte_read(p))? 'r':'-', \ + (pte_write(p))? 'w':'-', \ + (pte_exec(p))? 'x':'-', \ + (pte_dirty(p))? 'd':' ', \ + (pte_young(p))? 'a':' ', \ + (entry & _PAGE_USER)? 'u':'s', \ + " ", \ + (entry & _PAGE_GLOBAL)? 'g':' ', \ + (entry & _PAGE_PWT)? "wt":"wb", \ + (entry & _PAGE_PCD)? "cd":" "); +#if 0 +static int +display_pgdir(unsigned long addr, pgd_t *pgdir, int count) +{ + unsigned long entry; + int i; + int index = pgdir - ((pgd_t *)(((unsigned long)pgdir) & PAGE_MASK)); + + count = min(count, PTRS_PER_PGD - index); + addr &= ~(PGDIR_SIZE-1); + + for (i = 0; i < count; i++, pgdir++) { + entry = pgd_val(*pgdir); + kdb_printf("pgd: addr=%08lx ", addr); + if (pgd_none(*pgdir)) { + kdb_printf("pgdir not present\n"); + } else { + FORMAT_PGDIR(entry); + } + addr += PGDIR_SIZE; + } + return i; +} +#endif + +#if 0 /* for now, let's not print pgmiddle. */ +static int +display_pgmiddle(unsigned long addr, pmd_t *pgmiddle, int count) +{ + unsigned long entry; + int i; + int index = pgmiddle - ((pmd_t *)(((unsigned long)pgmiddle) & PAGE_MASK)); + + count = min(count, PTRS_PER_PMD - index); + addr &= ~(PMD_SIZE-1); + + for (i = 0; i < count; i++, pgmiddle++) { + entry = pmd_val(*pgmiddle); + kdb_printf("pmd: addr=%08lx ", addr); + if (pmd_none(*pgmiddle)) { + kdb_printf("pgmiddle not present\n"); + } else { + FORMAT_PGDIR(entry); + } + addr += PMD_SIZE; + } + return i; +} +#endif + +#if 0 +static int +display_pte(unsigned long addr, pte_t *pte, int count) +{ + unsigned long entry; + int i; + int index = pte - ((pte_t *)(((unsigned long)pte) & PAGE_MASK)); + + count = min(count, PTRS_PER_PTE - index); + addr &= PAGE_MASK; + + for (i = 0; i < count; i++, pte++) { + entry = pte_val(*pte); + kdb_printf("pte: addr=%08lx ", addr); + if (pte_none(*pte)) { + kdb_printf("pte not present\n"); + } else if (!pte_present(*pte)) { + kdb_printf("page swapped out. swp_offset=%08lx ", SWP_OFFSET(pte_to_swp_entry(*pte))); + kdb_printf("swp_type=%8lx", SWP_TYPE(pte_to_swp_entry(*pte))); + } else { + FORMAT_PTE(*pte, entry); + } + addr += PAGE_SIZE; + } + return i; +} + + +/* + * kdb_pte + * + * This function implements the 'pte' command. + * + * pte [] + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + */ +static int +kdb_pte(int argc, const char **argv) +{ + static unsigned long last_addr = 0, last_count = 0; + int count = 8; + unsigned long addr; + long offset = 0; + pgd_t *pgdir; + pmd_t *pgmiddle; + pte_t *pte; + +#ifdef CONFIG_X86_PAE + kdb_printf("This kernel is compiled with PAE support."); + return KDB_NOTIMP; +#endif + kdbgetintenv("MDCOUNT", &count); + + if (argc == 0) { + if (last_addr == 0) + return KDB_ARGCOUNT; + addr = last_addr; + if (last_count) + count = last_count; + } else { + kdb_machreg_t val; + int diag, nextarg = 1; + diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL); + if (diag) + return diag; + if (argc > nextarg+1) + return KDB_ARGCOUNT; + + if (argc >= nextarg) { + diag = kdbgetularg(argv[nextarg], &val); + if (!diag) { + count = (int) val; + last_count = count; + } else if (last_count) { + count = last_count; + } + } + } + + /* + * round off the addr to a page boundary. + */ + addr &= PAGE_MASK; + + get_pagetables(addr, &pgdir, &pgmiddle, &pte); + + if (pgdir) + display_pgdir(addr, pgdir, 1); +#if 0 /* for now, let's not print pgmiddle. */ + if (pgmiddle) + display_pgmiddle(addr, pgmiddle, 1); +#endif + if (pte) { + int displayed; + displayed = display_pte(addr, pte, count); + addr += (displayed << PAGE_SHIFT); + } + last_addr = addr; + return 0; +} +#else +/* + * Todo - In 2.5 the pte_offset macro in asm/pgtable.h seems to be + * renamed to pte_offset_kernel. + */ +static int +kdb_pte(int argc, const char **argv) +{ + kdb_printf("not supported."); + return KDB_NOTIMP; +} +#endif + +/* + * kdb_rdv + * + * This function implements the 'rdv' command. + * It displays all registers of the current processor + * included control registers in verbose mode. + * + * Inputs: + * argc argument count + * argv argument vector + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * This should have been an option to rd command say "rd v", + * but it is here as it is a non-essential x86-only command, + * that need not clutter arch/i386/kdb/kdbasupport.c. + */ +static int +kdb_rdv(int argc, const char **argv) +{ + struct pt_regs *regs = get_irq_regs(); + kdba_dumpregs(regs, NULL, NULL); + kdb_printf("\n"); + display_eflags(regs->flags); + kdb_printf("\n"); + display_gdtr(); + display_idtr(); + display_ldtr(); + kdb_printf("\n"); + display_cr0(); + display_cr3(); + display_cr4(); + display_cr8(); + kdb_printf("\n"); + display_dr(); + return 0; +} + +static int +kdb_rdmsr(int argc, const char **argv) +{ + unsigned long addr; + uint32_t l, h; + int diag; + struct cpuinfo_x86 *c = &cpu_data(smp_processor_id()); + + if (argc != 1) + return KDB_ARGCOUNT; + + if ((diag = kdbgetularg(argv[1], &addr))) + return diag; + + if (!cpu_has(c, X86_FEATURE_MSR)) + return KDB_NOTIMP; + + kdb_printf("msr(0x%lx) = ", addr); + if ((diag = rdmsr_safe(addr, &l, &h))) { + kdb_printf("error %d\n", diag); + return KDB_BADINT; + } else { + kdb_printf("0x%08x_%08x\n", h, l); + } + + return 0; +} + +static int +kdb_wrmsr(int argc, const char **argv) +{ + unsigned long addr; + unsigned long l, h; + int diag; + struct cpuinfo_x86 *c = &cpu_data(smp_processor_id()); + + if (argc != 3) + return KDB_ARGCOUNT; + + if ((diag = kdbgetularg(argv[1], &addr)) + || (diag = kdbgetularg(argv[2], &h)) + || (diag = kdbgetularg(argv[3], &l))) + return diag; + + if (!cpu_has(c, X86_FEATURE_MSR)) + return KDB_NOTIMP; + + if ((diag = wrmsr_safe(addr, l, h))) { + kdb_printf("error %d\n", diag); + return KDB_BADINT; + } + + return 0; +} + +static int __init kdbm_x86_init(void) +{ + kdb_register("rdv", kdb_rdv, NULL, "Display registers in verbose mode", 0); + kdb_register_repeat("gdt", kdb_gdt, " []", "Display GDT", 0, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("idt", kdb_idt, " []", "Display IDT", 0, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("ldt", kdb_ldt, " []", "Display LDT", 0, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("ptex", kdb_pte, " []", "Display pagetables", 0, KDB_REPEAT_NO_ARGS); + kdb_register_repeat("ldtp", kdb_ldt, " []", "Display Process LDT", 0, KDB_REPEAT_NO_ARGS); + kdb_register("rdmsr", kdb_rdmsr, "", "Display Model Specific Register", 0); + kdb_register("wrmsr", kdb_wrmsr, " ", "Modify Model Specific Register", 0); + return 0; +} + +static void __exit kdbm_x86_exit(void) +{ + kdb_unregister("rdv"); + kdb_unregister("gdt"); + kdb_unregister("ldt"); + kdb_unregister("idt"); + kdb_unregister("ptex"); + kdb_unregister("ldtp"); + kdb_unregister("rdmsr"); + kdb_unregister("wrmsr"); +} + +module_init(kdbm_x86_init) +module_exit(kdbm_x86_exit) --- /dev/null +++ b/kdb/modules/lcrash/README @@ -0,0 +1,3 @@ + + These files are copied from lcrash. + The only changes are flagged with "cpw". --- /dev/null +++ b/kdb/modules/lcrash/asm/README @@ -0,0 +1 @@ +This kl_types.h is asm-ia64 version. --- /dev/null +++ b/kdb/modules/lcrash/asm/kl_dump_ia64.h @@ -0,0 +1,199 @@ +/* + * $Id: kl_dump_ia64.h 1151 2005-02-23 01:09:12Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +/* This header file holds the architecture specific crash dump header */ +#ifndef __KL_DUMP_IA64_H +#define __KL_DUMP_IA64_H + +/* definitions */ +#ifndef KL_NR_CPUS +# define KL_NR_CPUS 128 /* max number CPUs */ +#endif + +#define KL_DUMP_MAGIC_NUMBER_IA64 0xdeaddeadULL /* magic number */ +#define KL_DUMP_VERSION_NUMBER_IA64 0x4 /* version number */ + + +/* + * mkswap.c calls getpagesize() to get the system page size, + * which is not necessarily the same as the hardware page size. + * + * For ia64 the kernel PAGE_SIZE can be configured from 4KB ... 16KB. + * + * The physical memory is layed out out in the hardware/minimal pages. + * This is the size we need to use for dumping physical pages. + * + * Note ths hardware/minimal page size being use in; + * arch/ia64/kernel/efi.c`efi_memmap_walk(): + * curr.end = curr.start + (md->num_pages << 12); + * + * Since the system page size could change between the kernel we boot + * on the the kernel that cause the core dume we may want to have something + * more constant like the maximum system page size (See include/asm-ia64/page.h). + */ +#define DUMP_MIN_PAGE_SHIFT 12 +#define DUMP_MIN_PAGE_SIZE (1UL << DUMP_MIN_PAGE_SHIFT) +#define DUMP_MIN_PAGE_MASK (~(DUMP_MIN_PAGE_SIZE - 1)) +#define DUMP_MIN_PAGE_ALIGN(addr) (((addr) + DUMP_MIN_PAGE_SIZE - 1) & DUMP_MIN_PAGE_MASK) + +#define DUMP_MAX_PAGE_SHIFT 16 +#define DUMP_MAX_PAGE_SIZE (1UL << DUMP_MAX_PAGE_SHIFT) +#define DUMP_MAX_PAGE_MASK (~(DUMP_MAX_PAGE_SIZE - 1)) +#define DUMP_MAX_PAGE_ALIGN(addr) (((addr) + DUMP_MAX_PAGE_SIZE - 1) & DUMP_MAX_PAGE_MASK) + +#define DUMP_HEADER_OFFSET DUMP_MAX_PAGE_SIZE + +#define DUMP_EF_PAGE_SHIFT DUMP_MIN_PAGE_SHIFT + +#define DUMP_PAGE_SHIFT DUMP_MIN_PAGE_SHIFT +#define DUMP_PAGE_SIZE DUMP_MIN_PAGE_SIZE +#define DUMP_PAGE_MASK DUMP_MIN_PAGE_MASK +#define DUMP_PAGE_ALIGN(addr) DUMP_MIN_PAGE_ALIGN(addr) + +struct kl_ia64_fpreg { + union { + unsigned long bits[2]; + long double __dummy; /* force 16-byte alignment */ + } u; +}; + +struct kl_pt_regs_ia64 { + /* for 2.6 kernels only. This structure was totally different in 2.4 kernels */ + unsigned long b6; /* scratch */ + unsigned long b7; /* scratch */ + + unsigned long ar_csd; /* used by cmp8xchg16 (scratch) */ + unsigned long ar_ssd; /* reserved for future use (scratch) */ + + unsigned long r8; /* scratch (return value register 0) */ + unsigned long r9; /* scratch (return value register 1) */ + unsigned long r10; /* scratch (return value register 2) */ + unsigned long r11; /* scratch (return value register 3) */ + + unsigned long cr_ipsr; /* interrupted task's psr */ + unsigned long cr_iip; /* interrupted task's instruction pointer */ + unsigned long cr_ifs; /* interrupted task's function state */ + + unsigned long ar_unat; /* interrupted task's NaT register (preserved) */ + unsigned long ar_pfs; /* prev function state */ + unsigned long ar_rsc; /* RSE configuration */ + /* The following two are valid only if cr_ipsr.cpl > 0: */ + unsigned long ar_rnat; /* RSE NaT */ + unsigned long ar_bspstore; /* RSE bspstore */ + + unsigned long pr; /* 64 predicate registers (1 bit each) */ + unsigned long b0; /* return pointer (bp) */ + unsigned long loadrs; /* size of dirty partition << 16 */ + + unsigned long r1; /* the gp pointer */ + unsigned long r12; /* interrupted task's memory stack pointer */ + unsigned long r13; /* thread pointer */ + + unsigned long ar_fpsr; /* floating point status (preserved) */ + unsigned long r15; /* scratch */ + + /* The remaining registers are NOT saved for system calls. */ + + unsigned long r14; /* scratch */ + unsigned long r2; /* scratch */ + unsigned long r3; /* scratch */ + + /* The following registers are saved by SAVE_REST: */ + unsigned long r16; /* scratch */ + unsigned long r17; /* scratch */ + unsigned long r18; /* scratch */ + unsigned long r19; /* scratch */ + unsigned long r20; /* scratch */ + unsigned long r21; /* scratch */ + unsigned long r22; /* scratch */ + unsigned long r23; /* scratch */ + unsigned long r24; /* scratch */ + unsigned long r25; /* scratch */ + unsigned long r26; /* scratch */ + unsigned long r27; /* scratch */ + unsigned long r28; /* scratch */ + unsigned long r29; /* scratch */ + unsigned long r30; /* scratch */ + unsigned long r31; /* scratch */ + + unsigned long ar_ccv; /* compare/exchange value (scratch) */ + + /* + * * Floating point registers that the kernel considers scratch: + * */ + struct kl_ia64_fpreg f6; /* scratch */ + struct kl_ia64_fpreg f7; /* scratch */ + struct kl_ia64_fpreg f8; /* scratch */ + struct kl_ia64_fpreg f9; /* scratch */ + struct kl_ia64_fpreg f10; /* scratch */ + struct kl_ia64_fpreg f11; /* scratch */ +} __attribute__((packed)); + +/* + * Structure: dump_header_asm_t + * Function: This is the header for architecture-specific stuff. It + * follows right after the dump header. + */ +typedef struct kl_dump_header_ia64_s { + /* the dump magic number -- unique to verify dump is valid */ + uint64_t magic_number; + /* the version number of this dump */ + uint32_t version; + /* the size of this header (in case we can't read it) */ + uint32_t header_size; + /* pointer to pt_regs */ + uint64_t pt_regs; + /* the dump registers */ + struct kl_pt_regs_ia64 regs; + /* the rnat register saved after flushrs */ + uint64_t rnat; + /* the pfs register saved after flushrs */ + uint64_t pfs; + /* the bspstore register saved after flushrs */ + uint64_t bspstore; + + /* smp specific */ + uint32_t smp_num_cpus; + uint32_t dumping_cpu; + struct kl_pt_regs_ia64 smp_regs[KL_NR_CPUS]; + uint64_t smp_current_task[KL_NR_CPUS]; + uint64_t stack[KL_NR_CPUS]; +} __attribute__((packed)) kl_dump_header_ia64_t; + +/* The following struct is used just to calculate the size needed + * to store per CPU info. (Make sure it is sync with the above struct) + */ +struct kl_dump_CPU_info_ia64 { + struct kl_pt_regs_ia64 smp_regs; + uint64_t smp_current_task; + uint64_t stack; +} __attribute__((packed)); + +/* function declarations + */ +int kl_set_dumparch_ia64(void); +uint32_t dha_num_cpus_ia64(void); +kaddr_t dha_current_task_ia64(int cpuid); +int dha_cpuid_ia64(kaddr_t); +kaddr_t dha_stack_ia64(int); +kaddr_t dha_stack_ptr_ia64(int); +int kl_read_dump_header_ia64(void); + +#endif /* __KL_DUMP_IA64_H */ --- /dev/null +++ b/kdb/modules/lcrash/asm/kl_types.h @@ -0,0 +1,48 @@ +/* + * $Id: kl_types.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __ASMIA64_KL_TYPES_H +#define __ASMIA64_KL_TYPES_H + +/* cpw */ +/* was #include */ +#include "kl_dump_ia64.h" + +#define HOST_ARCH_IA64 +/* cpw: add this, as otherwise comes from makefile */ +#define DUMP_ARCH_IA64 + +/* Format string that allows a single fprintf() call to work for both + * 32-bit and 64-bit pointer values (architecture specific). + */ +#ifdef CONFIG_X86_32 +#define FMT64 "ll" +#else +#define FMT64 "l" +#endif +#define FMTPTR "l" + +/* for usage in common code where host architecture + * specific type/macro is needed + */ +typedef kl_dump_header_ia64_t kl_dump_header_asm_t; +#define KL_DUMP_ASM_MAGIC_NUMBER KL_DUMP_MAGIC_NUMBER_IA64 + +#endif /* __ASMIA64_KL_TYPES_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_alloc.h @@ -0,0 +1,124 @@ +/* + * $Id: kl_alloc.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libutil. + * A library which provides auxiliary functions. + * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_ALLOC_H +#define __KL_ALLOC_H + +/** + ** Header file for kl_alloc.c module + ** + **/ + +#define K_TEMP 1 +#define K_PERM 2 + +/** function prototypes for register functions + **/ + +/* Memory block allocator. Returns a pointer to an allocated block + * of size bytes. In case of error, a NULL pointer will be returned + * and errno will be set to indicate exactly what error occurred. + * Note that the flag value will determine if the block allocated is + * temporary (can be freed via a call to kl_free_temp_blks()) or + * permenant (must be freed with a call to kl_free_block()).. + */ +typedef void * (*klib_block_alloc_func) ( + int /* size of block required */, + int /* flag value */, + void * /* return address */); + +/* Memory block reallocator. Returns a pointer to a block of new_size + * bytes. In case of error, a NULL pointer will be returned and + * errno will be set to indicate exactly what error occurred. + * Note that the flag value will determine if the block allocated is + * temporary (can be free via a call to kl_free_temp_blks()) or + * permenant. + */ +typedef void * (*klib_block_realloc_func) ( + void * /* pointer to block to realloc */, + int /* size of new block required */, + int /* flag value */, + void * /* return address */); + +/* Memory block duplicator. Returns a pointer to a block that is + * a copy of the block passed in via pointer. In case of error, a + * NULL pointer will be returned and errno will be set to indicate + * exactly what error occurred. Note that the flag value will + * determine if the block allocated is temporary (will be freed + * via a call to kl_free_temp_blks()) or permenant. Note that this + * function is only supported when liballoc is used (there is no + * way to tell the size of a malloced block. + */ +typedef void * (*klib_block_dup_func) ( + void * /* pointer to block to dup */, + int /* flag value */, + void * /* return address */); + +/* Allocates a block large enough to hold a string (plus the terminating + * NULL character). + */ +typedef void * (*klib_str_to_block_func) ( + char * /* pointer to character string */, + int /* flag value */, + void * /* return address */); + +/* Frees blocks that were previously allocated. + */ +typedef void (*klib_block_free_func) ( + void * /* pointer to block */); + +/* alloc block wrapper function table structure + */ +typedef struct alloc_functions_s { + int flag; /* Functions initialized? */ + klib_block_alloc_func block_alloc; /* Returns ptr to block */ + klib_block_realloc_func block_realloc; /* Returns ptr to new blk */ + klib_block_dup_func block_dup; /* Returns ptr to new blk */ + klib_str_to_block_func str_to_block; /* Returns ptr to new blk */ + klib_block_free_func block_free; /* Frees memory block */ +} alloc_functions_t; + +extern alloc_functions_t alloc_functions; + +/* Macros for accessing functions in alloc_functions table + */ +#define KL_BLOCK_ALLOC() (alloc_functions.block_alloc) +#define KL_BLOCK_REALLOC() (alloc_functions.block_realloc) +#define KL_BLOCK_DUP() (alloc_functions.block_dup) +#define KL_STR_TO_BLOCK() (alloc_functions.str_to_block) +#define KL_BLOCK_FREE() (alloc_functions.block_free) + +void *_kl_alloc_block(int, int, void *); +void *_kl_realloc_block(void *, int, int, void *); +void *_kl_dup_block(void *, int, void *); +void *_kl_str_to_block(char *, int, void *); +#if 0 +cpw: we create a new wrappers for these: +void kl_free_block(void *); + +#define kl_alloc_block(size, flags) _kl_alloc_block(size, flags, kl_get_ra()) +#endif +#define kl_realloc_block(b, new_size, flags) \ + _kl_realloc_block(b, new_size, flags, kl_get_ra()) +#define kl_dup_block(b, flags) _kl_dup_block(b, flags, kl_get_ra()) +#define kl_str_to_block(s, flags) _kl_str_to_block(s, flags, kl_get_ra()) + +#endif /* __KL_ALLOC_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_bfd.h @@ -0,0 +1,31 @@ +/* + * $Id: kl_bfd.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_BFD_H +#define __KL_BFD_H + +/* cpw: " " form: */ +#include "klib.h" + +int kl_check_bfd_error(bfd_error_type); +int kl_open_elf(char*, bfd**, bfd**); +int kl_read_bfd_syminfo(maplist_t*); + +#endif /* __KL_BFD_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_btnode.h @@ -0,0 +1,95 @@ +/* + * $Id: kl_btnode.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libutil. + * A library which provides auxiliary functions. + * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_BTNODE_H +#define __KL_BTNODE_H + +/* + * Node header struct for use in binary search tree routines + */ +typedef struct btnode_s { + struct btnode_s *bt_left; + struct btnode_s *bt_right; + struct btnode_s *bt_parent; + char *bt_key; + int bt_height; +} btnode_t; + +#define DUPLICATES_OK 1 + +/** + ** btnode operation function prototypes + **/ + +/* Return the hight of a given btnode_s struct in a tree. In the + * event of an error (a NULL btnode_s pointer was passed in), a + * value of -1 will be returned. + */ +int kl_btnode_height( + btnode_t* /* pointer to btnode_s struct */); + +/* Insert a btnode_s struct into a tree. After the insertion, the + * tree will be left in a reasonibly ballanced state. Note that, if + * the DUPLICATES_OK flag is set, duplicate keys will be inserted + * into the tree (otherwise return an error). In the event of an + * error, a value of -1 will be returned. + */ +int kl_insert_btnode( + btnode_t** /* pointer to root of tree */, + btnode_t* /* pointer to btnode_s struct to insert */, + int /* flags (DUPLICATES_OK) */); + +/* Finds a btnode in a tree and removes it, making sure to keep + * the tree in a reasonably balanced state. As part of the + * delete_btnode() operation, a call will be made to the free + * function (passed in as a parameter) to free any application + * specific data. + */ +int kl_delete_btnode( + btnode_t** /* pointer to the root of the btree */, + btnode_t* /* pointer to btnode_s struct to delete */, + void(*)(void*) /* pointer to function to actually free the node */, + int /* flags */); + +/* Traverse a tree looking for a particular key. In the event that + * duplicate keys are allowed in the tree, returns the first occurance + * of the search key found. A pointer to an int should be passed in + * to hold the maximum depth reached in the search. Upon success, + * returns a pointer to a btnode_s struct. Otherwise, a NULL pointer + * will be returned. + */ +btnode_t *_kl_find_btnode( + btnode_t* /* pointer to btnode_s struct to start search with */, + char* /* key we are looking for */, + int* /* pointer to where max depth vlaue will be placed */, + size_t /* if nonzero compare only first n chars of key */); +#define kl_find_btnode(A, B, C) _kl_find_btnode(A, B, C, 0) + +btnode_t *kl_first_btnode( + btnode_t * /* pointer to any btnode in a btree */); + +btnode_t *kl_next_btnode( + btnode_t * /* pointer to current btnode */); + +btnode_t *kl_prev_btnode( + btnode_t * /* Pointer to current btnode */); + +#endif /* __KL_BTNODE_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_cmp.h @@ -0,0 +1,102 @@ +/* + * $Id: kl_cmp.h 1216 2005-07-06 10:03:13Z holzheu $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_CMP_H +#define __KL_CMP_H + +#define DUMP_INDEX_MAGIC 0xdeadbeef +#define DUMP_INDEX_VERSION 31900 +#define NUM_BUCKETS 65535 + +/* + * Definitions for compressed cached reads. I've recently lowered + * these ... If they need to be increased later, I'll do so. + */ +#define CMP_HIGH_WATER_MARK 25 +#define CMP_LOW_WATER_MARK 10 + +#define CMP_VM_CACHED 0x01 +#define CMP_VM_UNCACHED 0x02 + + +/* + * This structure defines a page table entry, what each value will + * contain. Since these can be cached or uncached, we have a flags + * variable to specify this. + */ +typedef struct _ptableentry { + int flags; /* flags for page in cache */ + int length; /* length of page */ + int cached; /* cached (1 = yes, cached) */ + kaddr_t addr; /* addr of page */ + char *data; /* data in page */ + struct _ptableentry *next; /* ptr to next dump page */ + struct _ptableentry *prev; /* ptr to prev dump page */ + struct _ptableentry *nextcache; /* ptr to next cached page */ + struct _ptableentry *prevcache; /* ptr to prev cached page */ +} ptableentry; + +/* + * This is for the page table index from the compressed core dump. + * This is separate from the page table entries because these are + * simply addresses off of the compressed core dump, and not the + * actual data from the core dump. If we hash these values, we gain + * a lot of performance because we only have 1 to search for the + * page data, 1 to search for the index, and return if both searches + * failed. + */ +typedef struct _ptableindex { + kl_dump_page_t dir; /* directory entry of page */ + kaddr_t addr; /* address of page offset */ + kaddr_t coreaddr; /* address of page in core */ + unsigned int hash; /* hash value for this index item */ + struct _ptableindex *next; /* next pointer */ +} ptableindex; + +typedef struct dump_index_s { + unsigned int magic_number; /* dump index magic number */ + unsigned int version_number; /* dump index version number */ + /* struct timeval depends on machine, use two long values here */ + struct {uint64_t tv_sec; + uint64_t tv_usec; + } timebuf; /* the time of the dump */ +} __attribute__((packed)) dump_index_t; + +/* Compression function */ +typedef int (*kl_compress_fn_t)(const unsigned char *old, uint32_t old_size, unsigned char *new, uint32_t size); + +/* function declarations + */ +int kl_cmpreadmem(int, kaddr_t, char*, unsigned int, unsigned int); +int kl_cmpinit( + int /* fd */, + char * /* indexname */, + int /* flags */); + +/* Compression routine: No compression */ +int kl_compress_none(const char *old, uint32_t old_size, char *new, uint32_t new_size); + +/* Compression routine: Run length encoding */ +int kl_compress_rle(const char *old, uint32_t old_size, char *new, uint32_t new_size); + +/* Compression routine: GZIP */ +int kl_compress_gzip(const unsigned char *old, uint32_t old_size, unsigned char *new, uint32_t new_size); + +#endif /* __KL_CMP_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_copt.h @@ -0,0 +1,29 @@ +/* + * $Id: kl_copt.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libutil. + * A library which provides auxiliary functions. + * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. + * + * Created by Silicon Graphics, Inc. + * + * Copyright (C) 2003, 2004 Silicon Graphics, Inc. All rights reserved. + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ +#ifndef __KL_COPT_H +#define __KL_COPT_H + +extern int copt_ind; +extern char *copt_arg; +extern int copt_error; + +void reset_copt(void); +int is_copt(char *); +int get_copt(int, char **, const char *, char **); + +#endif /* __KL_COPT_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_debug.h @@ -0,0 +1,168 @@ +/* + * $Id: kl_debug.h 1196 2005-05-17 18:34:12Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_DEBUG_H +#define __KL_DEBUG_H + +/* generic functions for reading kerntypes in stabs and dwarf2 formats */ + +#define DBG_NONE 0 +#define DBG_STABS 1 +#define DBG_DWARF2 2 + +extern int debug_format; + +#define TYPE_NUM(X) ((uint64_t)(X) & 0xffffffff) +#define SRC_FILE(X) (((uint64_t)(X) >> 48) & 0xfff) +#define TYPE_NUM_SLOTS (255) +#define TYPE_NUM_HASH(X) \ + (((SRC_FILE(X)<<1)+TYPE_NUM(X)) % (TYPE_NUM_SLOTS - 1)) + +typedef struct dbg_type_s { + kltype_t st_klt; /* must be first */ + + int st_bit_offset; /* from start of struct/union */ + uint64_t st_type_num; /* DBG type_num */ + uint64_t st_real_type; /* real type type_num */ + uint64_t st_index_type; /* type_num of array index */ + uint64_t st_element_type; /* type_num of array element */ +} dbg_type_t; + +#define st_name st_klt.kl_name +#define st_type st_klt.kl_type +#define st_ptr st_klt.kl_ptr +#define st_flags st_klt.kl_flags +#define st_typestr st_klt.kl_typestr +#define st_size st_klt.kl_size +#define st_offset st_klt.kl_offset +#define st_low_bounds st_klt.kl_low_bounds +#define st_high_bounds st_klt.kl_high_bounds +#define st_value st_klt.kl_value +#define st_bit_size st_klt.kl_bit_size +#define st_next st_klt.kl_next +#define st_member st_klt.kl_member +#define st_realtype st_klt.kl_realtype +#define st_indextype st_klt.kl_indextype +#define st_elementtype st_klt.kl_elementtype +#define st_encoding st_klt.kl_encoding + +/* Structure containing information about a symbol entry + */ +/* this must match the definition in lkcd's libklib/include/kl_debug.h */ +typedef struct dbg_sym_s { + btnode_t sym_bt; /* must be first */ + short sym_dbgtyp; /* STABS, DWARF2, ... */ + short sym_state; /* current state */ + short sym_flag; /* current flag value */ + short sym_type; /* symbol type */ + short sym_pvttype; /* private type */ + short sym_nmlist; /* namelist index */ + short sym_srcfile; /* source file index */ + short sym_incfile; /* include file index */ + int sym_num; /* symbol number */ + int sym_off; /* symbol table offset */ + int sym_stroff; /* symbol offset in string table */ + uint64_t sym_typenum; /* arbitrary type number */ + kltype_t *sym_kltype; /* Full type information */ + struct dbg_sym_s *sym_next; /* next pointer for chaining */ + struct dbg_sym_s *sym_link; /* another pointer for chaining */ + int sym_dup; /* duplicate symbol */ +} dbg_sym_t; +#define sym_name sym_bt.bt_key + +extern dbg_sym_t *type_tree; +extern dbg_sym_t *typedef_tree; +extern dbg_sym_t *func_tree; +extern dbg_sym_t *srcfile_tree; +extern dbg_sym_t *var_tree; +extern dbg_sym_t *xtype_tree; +extern dbg_sym_t *symlist; +extern dbg_sym_t *symlist_end; + +/* State flags + */ +#define DBG_SETUP 0x1 +#define DBG_SETUP_DONE 0x2 +#define DBG_SETUP_FAILED 0x4 + +/* Flags for identifying individual symbol types + */ +#define DBG_SRCFILE 0x0001 +#define DBG_TYPE 0x0002 +#define DBG_TYPEDEF 0x0004 +#define DBG_FUNC 0x0008 +#define DBG_PARAM 0x0010 +#define DBG_LINE 0x0020 +#define DBG_VAR 0x0040 +#define DBG_XTYPE 0x0100 +#define DBG_ALL 0xffff + +/* Structure for cross referencing one type number to another + */ +typedef struct dbg_hashrec_s { + uint64_t h_typenum; /* type number */ + dbg_sym_t *h_ptr; /* pointer to actual type */ + struct dbg_hashrec_s *h_next; /* next pointer (for hashing) */ +} dbg_hashrec_t; + +extern dbg_hashrec_t *dbg_hash[]; + +#define HASH_SYM 1 +#define HASH_XREF 2 + +/* DBG function prototypes + */ +dbg_sym_t *dbg_alloc_sym( + int /* format */); + +void dbg_free_sym( + dbg_sym_t * /* dbg_sym_s pointer */); + +int dbg_setup_typeinfo( + dbg_sym_t * /* dbg_sym_s pointer */); + +int dbg_insert_sym( + dbg_sym_t * /* dbg_sym_s pointer */); + +void dbg_hash_sym( + uint64_t /* typenum */, + dbg_sym_t * /* dbg_sym_s pointer */); + +dbg_type_t *dbg_walk_hash( + int * /* pointer to hash index */, + void ** /* pointer to hash record pointer */); + +dbg_sym_t *dbg_find_sym( + char * /* name */, + int /* type number */, + uint64_t /* typenum */); + +dbg_sym_t *dbg_first_sym( + int /* type number */); + +dbg_sym_t *dbg_next_sym( + dbg_sym_t * /* dbg_sym_s pointer */); + +dbg_sym_t *dbg_prev_sym( + dbg_sym_t * /* dbg_sym_s pointer */); + +dbg_type_t *dbg_find_typenum( + uint64_t /* typenum */); + +#endif /* __KL_DEBUG_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_dump.h @@ -0,0 +1,511 @@ +/* + * $Id: kl_dump.h 1336 2006-10-23 23:27:06Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_DUMP_H +#define __KL_DUMP_H + +#if 0 +cpw: dont need: +#include +#include +#endif + +/* + * DUMP_DEBUG: a debug level for the kernel dump code and + * the supporting lkcd libraries in user space. + * + * 0: FALSE: No Debug Added + * 1: TRUE: Break Points + * . + * . + * . + * 6: Add Debug Data to Structures + * . + * . + * 9: Max + */ +#define DUMP_DEBUG FALSE + +#if DUMP_DEBUG +void dump_bp(void); /* Called when something exceptional occures */ +# define DUMP_BP() dump_bp() /* BreakPoint */ +#else +# define DUMP_BP() +#endif + + +#define KL_UTS_LEN 65 /* do not change ... */ + +extern int SN2_24X; + +/* + * Size of the buffer that's used to hold: + * + * 1. the dump header (paded to fill the complete buffer) + * 2. the possibly compressed page headers and data + */ +extern uint64_t KL_DUMP_BUFFER_SIZE; +extern uint64_t KL_DUMP_HEADER_SIZE; + +#if 0 +/* Variables that contain page size, mask etc. used in dump format + * (this is not the system page size stored in the dump header) + */ +uint64_t KL_DUMP_PAGE_SIZE; +uint64_t KL_DUMP_PAGE_MASK; +uint64_t KL_DUMP_PAGE_SHIFT; +#endif + +/* Dump header offset changed from 4k to 64k to support multiple page sizes */ +#define KL_DUMP_HEADER_OFFSET (1ULL << 16) + + +/* header definitions for dumps from s390 standalone dump tools */ +#define KL_DUMP_MAGIC_S390SA 0xa8190173618f23fdULL /* s390sa magic number */ +#define KL_DUMP_HEADER_SZ_S390SA 4096 + +/* standard header definitions */ +#define KL_DUMP_MAGIC_NUMBER 0xa8190173618f23edULL /* dump magic number */ +#define KL_DUMP_MAGIC_LIVE 0xa8190173618f23cdULL /* live magic number */ +#define KL_DUMP_MAGIC_ASM 0xdeaddeadULL /* generic arch magic number */ +#define KL_DUMP_VERSION_NUMBER 0x8 /* dump version number */ +#define KL_DUMP_PANIC_LEN 0x100 /* dump panic string length */ + +/* dump levels - type specific stuff added later -- add as necessary */ +#define KL_DUMP_LEVEL_NONE 0x0 /* no dumping at all -- just bail */ +#define KL_DUMP_LEVEL_HEADER 0x1 /* kernel dump header only */ +#define KL_DUMP_LEVEL_KERN 0x2 /* dump header and kernel pages */ +#define KL_DUMP_LEVEL_USED 0x4 /* dump header, kernel/user pages */ +#define KL_DUMP_LEVEL_ALL_RAM 0x8 /* dump header, all RAM pages */ +#define KL_DUMP_LEVEL_ALL 0x10 /* dump all memory RAM and firmware */ + +/* dump compression options -- add as necessary */ +#define KL_DUMP_COMPRESS_NONE 0x0 /* don't compress this dump */ +#define KL_DUMP_COMPRESS_RLE 0x1 /* use RLE compression */ +#define KL_DUMP_COMPRESS_GZIP 0x2 /* use GZIP compression */ + +/* dump flags - any dump-type specific flags -- add as necessary */ +#define KL_DUMP_FLAGS_NONE 0x0 /* no flags are set for this dump */ +#define KL_DUMP_FLAGS_NONDISRUPT 0x1 /* try to keep running after dump */ +#define KL_DUMP_FLAGS_DISKDUMP 0x80000000 /* dump to local disk */ +#define KL_DUMP_FLAGS_NETDUMP 0x40000000 /* dump to network device */ + +/* dump header flags -- add as necessary */ +#define KL_DUMP_DH_FLAGS_NONE 0x0 /* no flags set (error condition!) */ +#define KL_DUMP_DH_RAW 0x1 /* raw page (no compression) */ +#define KL_DUMP_DH_COMPRESSED 0x2 /* page is compressed */ +#define KL_DUMP_DH_END 0x4 /* end marker on a full dump */ +#define KL_DUMP_DH_TRUNCATED 0x8 /* dump is incomplete */ +#define KL_DUMP_DH_TEST_PATTERN 0x10 /* dump page is a test pattern */ +#define KL_DUMP_DH_NOT_USED 0x20 /* 1st bit not used in flags */ + +/* dump ioctl() control options */ +#ifdef IOCTL26 +#define DIOSDUMPDEV _IOW('p', 0xA0, unsigned int) /* set the dump device */ +#define DIOGDUMPDEV _IOR('p', 0xA1, unsigned int) /* get the dump device */ +#define DIOSDUMPLEVEL _IOW('p', 0xA2, unsigned int) /* set the dump level */ +#define DIOGDUMPLEVEL _IOR('p', 0xA3, unsigned int) /* get the dump level */ +#define DIOSDUMPFLAGS _IOW('p', 0xA4, unsigned int) /* set the dump flag parameters */ +#define DIOGDUMPFLAGS _IOR('p', 0xA5, unsigned int) /* get the dump flag parameters */ +#define DIOSDUMPCOMPRESS _IOW('p', 0xA6, unsigned int) /* set the dump compress level */ +#define DIOGDUMPCOMPRESS _IOR('p', 0xA7, unsigned int) /* get the dump compress level */ + +/* these ioctls are used only by netdump module */ +#define DIOSTARGETIP _IOW('p', 0xA8, unsigned int) /* set the target m/c's ip */ +#define DIOGTARGETIP _IOR('p', 0xA9, unsigned int) /* get the target m/c's ip */ +#define DIOSTARGETPORT _IOW('p', 0xAA, unsigned int) /* set the target m/c's port */ +#define DIOGTARGETPORT _IOR('p', 0xAB, unsigned int) /* get the target m/c's port */ +#define DIOSSOURCEPORT _IOW('p', 0xAC, unsigned int) /* set the source m/c's port */ +#define DIOGSOURCEPORT _IOR('p', 0xAD, unsigned int) /* get the source m/c's port */ +#define DIOSETHADDR _IOW('p', 0xAE, unsigned int) /* set ethernet address */ +#define DIOGETHADDR _IOR('p', 0xAF, unsigned int) /* get ethernet address */ +#define DIOGDUMPOKAY _IOR('p', 0xB0, unsigned int) /* check if dump is configured */ +#define DIOSDUMPTAKE _IOW('p', 0xB1, unsigned int) /* take a manual dump */ +#else +#define DIOSDUMPDEV 1 /* set the dump device */ +#define DIOGDUMPDEV 2 /* get the dump device */ +#define DIOSDUMPLEVEL 3 /* set the dump level */ +#define DIOGDUMPLEVEL 4 /* get the dump level */ +#define DIOSDUMPFLAGS 5 /* set the dump flag parameters */ +#define DIOGDUMPFLAGS 6 /* get the dump flag parameters */ +#define DIOSDUMPCOMPRESS 7 /* set the dump compress level */ +#define DIOGDUMPCOMPRESS 8 /* get the dump compress level */ +#define DIOSTARGETIP 9 /* set the target m/c's ip */ +#define DIOGTARGETIP 10 /* get the target m/c's ip */ +#define DIOSTARGETPORT 11 /* set the target m/c's port */ +#define DIOGTARGETPORT 12 /* get the target m/c's port */ +#define DIOSSOURCEPORT 13 /* set the source m/c's port */ +#define DIOGSOURCEPORT 14 /* get the source m/c's port */ +#define DIOSETHADDR 15 /* set ethernet address */ +#define DIOGETHADDR 16 /* get ethernet address */ +#define DIOGDUMPOKAY 17 /* check if dump is configured */ +#define DIOSDUMPTAKE 18 /* take a manual dump */ +#endif + +/* + * structures + */ + +/* This is the header dumped at the top of every valid crash dump. + */ +typedef struct kl_dump_header_s { + uint64_t magic_number; /* dump magic number, unique to verify dump */ + uint32_t version; /* version number of this dump */ + uint32_t header_size; /* size of this header */ + uint32_t dump_level; /* level of this dump */ + /* FIXME: rename page_size to dump_page_size + * The size of a hardware/physical memory page (DUMP_PAGE_SIZE). + * NB: Not the configurable system page (PAGE_SIZE) (4K, 8K, 16K, etc.) + */ +/* uint32_t dh_dump_page_size; */ + uint32_t page_size; /* page size (e.g. 4K, 8K, 16K, etc.) */ + uint64_t memory_size; /* size of entire physical memory */ + uint64_t memory_start; /* start of physical memory */ + uint64_t memory_end; /* end of physical memory */ +#if DUMP_DEBUG >= 6 + uint64_t num_bytes; /* number of bytes in this dump */ +#endif + /* the number of dump pages in this dump specifically */ + uint32_t num_dump_pages; + char panic_string[KL_DUMP_PANIC_LEN]; /* panic string, if available*/ + + /* timeval depends on machine, two long values */ + struct {uint64_t tv_sec; + uint64_t tv_usec; + } time; /* the time of the system crash */ + + /* the NEW utsname (uname) information -- in character form */ + /* we do this so we don't have to include utsname.h */ + /* plus it helps us be more architecture independent */ + char utsname_sysname[KL_UTS_LEN]; + char utsname_nodename[KL_UTS_LEN]; + char utsname_release[KL_UTS_LEN]; + char utsname_version[KL_UTS_LEN]; + char utsname_machine[KL_UTS_LEN]; + char utsname_domainname[KL_UTS_LEN]; + + uint64_t current_task; /* fixme: better use uint64_t here */ + uint32_t dump_compress; /* compression type used in this dump */ + uint32_t dump_flags; /* any additional flags */ + uint32_t dump_device; /* any additional flags */ + uint64_t dump_buffer_size; /* version >= 9 */ +} __attribute__((packed)) kl_dump_header_t; + +/* This is the header used by the s390 standalone dump tools + */ +typedef struct kl_dump_header_s390sa_s { + uint64_t magic_number; /* magic number for this dump (unique)*/ + uint32_t version; /* version number of this dump */ + uint32_t header_size; /* size of this header */ + uint32_t dump_level; /* the level of this dump (just a header?) */ + uint32_t page_size; /* page size of dumped Linux (4K,8K,16K etc.) */ + uint64_t memory_size; /* the size of all physical memory */ + uint64_t memory_start; /* the start of physical memory */ + uint64_t memory_end; /* the end of physical memory */ + uint32_t num_pages; /* number of pages in this dump */ + uint32_t pad; /* ensure 8 byte alignment for tod and cpu_id */ + uint64_t tod; /* the time of the dump generation */ + uint64_t cpu_id; /* cpu id */ + uint32_t arch_id; + uint32_t build_arch_id; +#define KL_DH_ARCH_ID_S390X 2 +#define KL_DH_ARCH_ID_S390 1 +} __attribute__((packed)) kl_dump_header_s390sa_t; + +/* Header associated to each physical page of memory saved in the system + * crash dump. + */ +typedef struct kl_dump_page_s { +#if DUMP_DEBUG >= 6 + uint64_t byte_offset; /* byte offset */ + uint64_t page_index; /* page index */ +#endif + uint64_t address; /* the address of this dump page */ + uint32_t size; /* the size of this dump page */ + uint32_t flags; /* flags (DUMP_COMPRESSED, DUMP_RAW or DUMP_END) */ +} __attribute__((packed)) kl_dump_page_t; + +/* CORE_TYPE indicating type of dump + */ +typedef enum { + dev_kmem, /* image of /dev/kmem, a running kernel */ + reg_core, /* Regular (uncompressed) core file */ + s390_core, /* s390 core file */ + cmp_core, /* compressed core file */ + unk_core /* unknown core type */ +} CORE_TYPE; + +/* function to determine kernel stack for task */ +typedef kaddr_t(*kl_kernelstack_t) (kaddr_t); +/* map virtual address to physical one */ +typedef int(*kl_virtop_t)(kaddr_t, void*, kaddr_t*); +/* function to perform page-table traversal */ +typedef kaddr_t(*kl_mmap_virtop_t)(kaddr_t, void*); +/* XXX description */ +typedef int(*kl_valid_physmem_t)(kaddr_t, int); +/* XXX description */ +typedef kaddr_t(*kl_next_valid_physaddr_t)(kaddr_t); +/* write a dump-header-asm, if analyzing a live system */ +typedef int(*kl_write_dump_header_asm_t)(void*); +/* redirect addresses pointing into task_union areas for running tasks */ +typedef kaddr_t(*kl_fix_vaddr_t)(kaddr_t, size_t); +/* initialize mapping of virtual to physical addresses */ +typedef int (*kl_init_virtop_t)(void); + +/* struct storing dump architecture specific values + */ +typedef struct kl_dumparch_s { + int arch; /* KL_ARCH_ */ + int ptrsz; /* 32 or 64 bit */ + int byteorder; /* KL_LITTLE_ENDIAN or KL_BIG_ENDIAN */ + uint64_t pageoffset; /* PAGE_OFFSET */ + uint64_t kstacksize; /* size of kernel stack */ + uint64_t pgdshift; /* PGDIR_SHIFT */ + uint64_t pgdsize; /* PGDIR_SIZE */ + uint64_t pgdmask; /* PGDIR_MASK */ + uint64_t pmdshift; /* PMD_SHIFT */ + uint64_t pmdsize; /* PMD_SIZE */ + uint64_t pmdmask; /* PMD_MASK */ + uint64_t pageshift; /* PAGE_SHIFT */ + uint64_t pagesize; /* PAGE_SIZE */ + uint64_t pagemask; /* PAGE_MASK */ + uint32_t ptrsperpgd; /* PTRS_PER_PGD */ + uint32_t ptrsperpmd; /* PTRS_PER_PMD */ + uint32_t ptrsperpte; /* PTRS_PER_PTE */ + kl_kernelstack_t kernelstack; /* determine kernel stack for task */ + kl_virtop_t virtop; /* map virtual address to physical */ + kl_mmap_virtop_t mmap_virtop; /* traverse page table */ + kl_valid_physmem_t valid_physmem; /* XXX description */ + kl_next_valid_physaddr_t next_valid_physaddr; /* XXX description */ + kl_fix_vaddr_t fix_vaddr; /* XXX description */ + uint32_t dha_size; /* size of kl_dump_header_xxx_t */ + kl_write_dump_header_asm_t write_dha; /* XXX description */ + kl_init_virtop_t init_virtop; /* init address translation */ +} kl_dumparch_t; + +/* function types for dumpaccess */ +typedef kaddr_t (*kl_get_ptr_t) (void*); +typedef uint8_t (*kl_get_uint8_t) (void*); +typedef uint16_t(*kl_get_uint16_t)(void*); +typedef uint32_t(*kl_get_uint32_t)(void*); +typedef uint64_t(*kl_get_uint64_t)(void*); +/* function types for dumpaccess */ +typedef kaddr_t (*kl_read_ptr_t) (kaddr_t); +typedef uint8_t (*kl_read_uint8_t) (kaddr_t); +typedef uint16_t (*kl_read_uint16_t)(kaddr_t); +typedef uint32_t (*kl_read_uint32_t)(kaddr_t); +typedef uint64_t (*kl_read_uint64_t)(kaddr_t); + +/* struct to store dump architecture specific functions + */ +typedef struct kl_dumpaccess_s { + /* get integer value from memory, previously read from dump */ + kl_get_ptr_t get_ptr; + kl_get_uint8_t get_uint8; + kl_get_uint16_t get_uint16; + kl_get_uint32_t get_uint32; + kl_get_uint64_t get_uint64; + /* read integer value from dump (from physical address) */ + kl_read_ptr_t read_ptr; + kl_read_uint8_t read_uint8; + kl_read_uint16_t read_uint16; + kl_read_uint32_t read_uint32; + kl_read_uint64_t read_uint64; + /* read integer value from dump (from virtual address) */ + kl_read_ptr_t vread_ptr; + kl_read_uint8_t vread_uint8; + kl_read_uint16_t vread_uint16; + kl_read_uint32_t vread_uint32; + kl_read_uint64_t vread_uint64; +} kl_dumpaccess_t; + +/* Struct containing sizes of frequently used kernel structures. + */ +typedef struct struct_sizes_s { + int task_struct_sz; + int mm_struct_sz; + int page_sz; + int module_sz; + int new_utsname_sz; + int switch_stack_sz; + int pt_regs_sz; + int pglist_data_sz; + int runqueue_sz; +} struct_sizes_t; + +/* struct storing memory specifc values of the dumped Linux system + */ +typedef struct kl_kerninfo_s{ + kaddr_t num_physpages; /* number of physical pages */ + kaddr_t mem_map; /* XXX description */ + kaddr_t high_memory; /* physical memory size */ + kaddr_t init_mm; /* address of mm_struct init_mm */ + uint64_t kernel_flags; /* to indicate kernel features + * e.g. KL_IS_PAE_I386 on i386 */ + int num_cpus; /* number of cpus */ + kaddr_t pgdat_list; /* pgdat_list value. used as MEM_MAP */ + /* not defined for DISCONTIG memory */ + int linux_release; /* kernel release of dump */ + struct_sizes_t struct_sizes; /* frequently needed struct sizes */ +} kl_kerninfo_t; + +/* various flags to indicate Linux kernel compile switches */ +#define KL_IS_PAE_I386 0x0020 /* i386 kernel with PAE support */ + +/* struct where to keep whole information about the dump + */ +typedef struct kl_dumpinfo_s { + CORE_TYPE core_type; /* type of core file */ + char *dump; /* pathname for dump */ + char *map; /* pathname for map file */ + int core_fd; /* file descriptor for dump file */ + int rw_flag; /* O_RDONLY/O_RDWR (/dev/kmem only) */ + kl_dumparch_t arch; /* dump arch info */ + kl_dumpaccess_t func; /* dump access functions */ + kl_kerninfo_t mem; /* mem info for dump */ +} kl_dumpinfo_t; + +/* External declarations + */ +extern char *dh_typename; +extern char *dha_typename; +extern void *G_dump_header; +extern void *G_dump_header_asm; +extern kl_dump_header_t *KL_DUMP_HEADER; +extern void *KL_DUMP_HEADER_ASM; + +/* function declarations + */ + +/* open dump */ +int kl_open_dump(void); + +/* init sizes for some structures */ +void kl_init_struct_sizes(void); + +/* init host architecture information */ +int kl_setup_hostinfo(void); + +/* init dumpinfo structure */ +int kl_setup_dumpinfo(char * /* map file */, + char * /* dump */, + int /* rwflag */); + + +/* init dumpinfo structure */ +int kl_set_dumpinfo(char * /* map file */, + char * /* dump */, + int /* arch of dump */, + int /* rwflag */); + +/* free dumpinfo structure */ +void kl_free_dumpinfo(kl_dumpinfo_t *); + +/* set memory related characteristics of dump */ +int kl_set_kerninfo(void); + +/* set function pointers for dump access (depends on host and dump arch) */ +int kl_set_dumpaccess(void); + +/* print contents of kl_dumpinfo_t etc. */ +int kl_print_dumpinfo(int); +#define KL_INFO_ALL 0 +#define KL_INFO_ENDIAN 1 +#define KL_INFO_ARCH 2 +#define KL_INFO_PTRSZ 3 +#define KL_INFO_KRELEASE 4 +#define KL_INFO_MEMSIZE 5 +#define KL_INFO_NUMCPUS 6 + +/* Functions that read data from generic dump_header */ +int kl_valid_dump_magic(uint64_t); +int kl_header_swap(void *); +uint64_t kl_header_magic(void *); +int kl_valid_header(void *); +uint32_t kl_header_version(void *); +int kl_header_size(void *); +void *kl_read_header(int fd, void *); + +/* init common lkcd dump header from dump */ +void kl_init_dump_header(int); + +/* try to evalutate arch from lkcd 4.1 (version <= 7) dump header */ +int kl_dump_arch_4_1(void *); + +/* swap dump header values if necessary */ +void kl_swap_dump_header_reg(kl_dump_header_t* dh); +void kl_swap_dump_header_s390sa(kl_dump_header_s390sa_t* dh); + +/* Read dump header in from dump */ +int kl_read_dump_header(void); +int kl_read_dump_header_asm(void); + +/* Determine the architecure of dump */ +int kl_set_dumparch(int); + +/* Finish setting up for access to dump */ +int kl_setup_dumpaccess(int); + +/* get the raw dump header */ +int kl_get_raw_dh(int); +int kl_get_raw_asm_dh(int); + +/* get common lkcd dump header */ +int kl_get_dump_header(kl_dump_header_t*); + +/* get older style dump headers */ +kl_dump_header_t *get_dump_header_4_1(void *); +kl_dump_header_t *get_dump_header_SN2_24X(void *); + +/* get task that was running when dump was started */ +kaddr_t kl_dumptask(void); + +/* Print dump header */ +int kl_print_dump_header(const char* dump); + +/* Print dump regular header */ +void kl_print_dump_header_reg(kl_dump_header_t *); + +/* Print s390 dump header */ +void kl_print_dump_header_s390(char*); + +/* Convert s390 to reg header */ +void kl_s390sa_to_reg_header(kl_dump_header_s390sa_t*, kl_dump_header_t*); + +/* Byte swapping functions needed for Xclrash */ +/* get integer value from buffer and swap bytes */ +kaddr_t kl_get_swap_ptr(void*); +uint16_t kl_get_swap_uint16(void*); +uint32_t kl_get_swap_uint32(void*); +uint64_t kl_get_swap_uint64(void*); + +/* read integer value from dump (physical address) and swap bytes */ +kaddr_t kl_read_swap_ptr(kaddr_t); +uint16_t kl_read_swap_uint16(kaddr_t); +uint32_t kl_read_swap_uint32(kaddr_t); +uint64_t kl_read_swap_uint64(kaddr_t); + +/* read integer value from dump (virtual address) and swap bytes */ +kaddr_t kl_vread_swap_ptr(kaddr_t); +uint16_t kl_vread_swap_uint16(kaddr_t); +uint32_t kl_vread_swap_uint32(kaddr_t); +uint64_t kl_vread_swap_uint64(kaddr_t); + +#endif /* __KL_DUMP_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_dump_arch.h @@ -0,0 +1,124 @@ +/* + * $Id: kl_dump_arch.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_DUMP_ARCH_H +#define __KL_DUMP_ARCH_H + +/* check for valid configuration + */ +#if !(defined(HOST_ARCH_ALPHA) || defined(HOST_ARCH_I386) || \ + defined(HOST_ARCH_IA64) || defined(HOST_ARCH_S390) || \ + defined(HOST_ARCH_S390X) || defined(HOST_ARCH_ARM) || \ + defined(HOST_ARCH_PPC64) || defined(HOST_ARCH_X86_64)) +# error "No valid host architecture defined." +#endif +#if ((defined(HOST_ARCH_ALPHA) && \ + (defined(HOST_ARCH_I386) || defined(HOST_ARCH_IA64) || \ + defined(HOST_ARCH_S390) || defined(HOST_ARCH_S390X) || \ + defined(HOST_ARCH_ARM) || defined(HOST_ARCH_PPC64) || \ + defined(HOST_ARCH_X86_64))) || \ + (defined(HOST_ARCH_I386) && \ + (defined(HOST_ARCH_IA64) || defined(HOST_ARCH_S390) || \ + defined(HOST_ARCH_S390X)|| defined(HOST_ARCH_ARM) || \ + defined(HOST_ARCH_PPC64)|| defined(HOST_ARCH_X86_64))) || \ + (defined(HOST_ARCH_IA64) && \ + (defined(HOST_ARCH_S390)|| defined(HOST_ARCH_S390X) || \ + defined(HOST_ARCH_ARM) || defined(HOST_ARCH_PPC64) || \ + defined(HOST_ARCH_X86_64))) || \ + (defined(HOST_ARCH_S390) && \ + (defined(HOST_ARCH_S390X) || defined(HOST_ARCH_ARM) || \ + defined(HOST_ARCH_PPC64) || defined(HOST_ARCH_X86_64))) || \ + (defined(HOST_ARCH_S390X) && \ + (defined(HOST_ARCH_ARM) || defined(HOST_ARCH_PPC64) || \ + defined(HOST_ARCH_X86_64))) || \ + (defined(HOST_ARCH_ARM) && \ + (defined(HOST_ARCH_PPC64) || defined(HOST_ARCH_X86_64))) || \ + (defined(HOST_ARCH_PPC64) && defined(HOST_ARCH_X86_64))) +# error "More than one valid host architectures defined." +#endif +#if !(defined(DUMP_ARCH_ALPHA) || defined(DUMP_ARCH_I386) || \ + defined(DUMP_ARCH_IA64) || defined(DUMP_ARCH_S390) || \ + defined(DUMP_ARCH_S390X) || defined(DUMP_ARCH_ARM) || \ + defined(DUMP_ARCH_PPC64) || defined(DUMP_ARCH_X86_64)) +# error "No valid dump architecture defined." +#endif + +/* optional: check that host arch equals one supported dump arch + */ +#ifdef SUPPORT_HOST_ARCH +# if (defined(HOST_ARCH_ALPHA) && !defined(DUMP_ARCH_ALPHA)) || \ + (defined(HOST_ARCH_I386) && !defined(DUMP_ARCH_I386)) || \ + (defined(HOST_ARCH_IA64) && !defined(DUMP_ARCH_IA64)) || \ + (defined(HOST_ARCH_S390) && !defined(DUMP_ARCH_S390)) || \ + (defined(HOST_ARCH_S390X) && !defined(DUMP_ARCH_S390X)) || \ + (defined(HOST_ARCH_ARM) && !defined(DUMP_ARCH_ARM)) || \ + (defined(HOST_ARCH_PPC64) && !defined(DUMP_ARCH_PPC64)) || \ + (defined(HOST_ARCH_X86_64) && !defined(DUMP_ARCH_X86_64)) +# error "Host architecture not supported as dump architecture." +# endif +#endif + +/* include dump architecture specific stuff + */ +#ifdef DUMP_ARCH_ALPHA +# include +# include +#endif +/* cpw: use the " " form: */ +#ifdef DUMP_ARCH_IA64 +# include "kl_mem_ia64.h" +# include "kl_dump_ia64.h" +#endif +#ifdef DUMP_ARCH_I386 +# include +# include +#endif +#ifdef DUMP_ARCH_S390 +# include +# include +#endif +#ifdef DUMP_ARCH_S390X +# include +# include +#endif +#ifdef DUMP_ARCH_ARM +# include +# include +#endif +#ifdef DUMP_ARCH_PPC64 +#include +#include +#endif +#ifdef DUMP_ARCH_X86_64 +#include +#include +#endif + +/** Function prototypes + **/ +int kl_init_kern_info(void); + +int kl_get_struct( + kaddr_t /* address */, + int /* size of struct */, + void * /* ptr to buffer */, + char * /* name of struct */); + +#endif /* __KL_DUMP_ARCH_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_dump_ia64.h @@ -0,0 +1,199 @@ +/* + * $Id: kl_dump_ia64.h 1151 2005-02-23 01:09:12Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +/* This header file holds the architecture specific crash dump header */ +#ifndef __KL_DUMP_IA64_H +#define __KL_DUMP_IA64_H + +/* definitions */ +#ifndef KL_NR_CPUS +# define KL_NR_CPUS 128 /* max number CPUs */ +#endif + +#define KL_DUMP_MAGIC_NUMBER_IA64 0xdeaddeadULL /* magic number */ +#define KL_DUMP_VERSION_NUMBER_IA64 0x4 /* version number */ + + +/* + * mkswap.c calls getpagesize() to get the system page size, + * which is not necessarily the same as the hardware page size. + * + * For ia64 the kernel PAGE_SIZE can be configured from 4KB ... 16KB. + * + * The physical memory is layed out out in the hardware/minimal pages. + * This is the size we need to use for dumping physical pages. + * + * Note ths hardware/minimal page size being use in; + * arch/ia64/kernel/efi.c`efi_memmap_walk(): + * curr.end = curr.start + (md->num_pages << 12); + * + * Since the system page size could change between the kernel we boot + * on the the kernel that cause the core dume we may want to have something + * more constant like the maximum system page size (See include/asm-ia64/page.h). + */ +#define DUMP_MIN_PAGE_SHIFT 12 +#define DUMP_MIN_PAGE_SIZE (1UL << DUMP_MIN_PAGE_SHIFT) +#define DUMP_MIN_PAGE_MASK (~(DUMP_MIN_PAGE_SIZE - 1)) +#define DUMP_MIN_PAGE_ALIGN(addr) (((addr) + DUMP_MIN_PAGE_SIZE - 1) & DUMP_MIN_PAGE_MASK) + +#define DUMP_MAX_PAGE_SHIFT 16 +#define DUMP_MAX_PAGE_SIZE (1UL << DUMP_MAX_PAGE_SHIFT) +#define DUMP_MAX_PAGE_MASK (~(DUMP_MAX_PAGE_SIZE - 1)) +#define DUMP_MAX_PAGE_ALIGN(addr) (((addr) + DUMP_MAX_PAGE_SIZE - 1) & DUMP_MAX_PAGE_MASK) + +#define DUMP_HEADER_OFFSET DUMP_MAX_PAGE_SIZE + +#define DUMP_EF_PAGE_SHIFT DUMP_MIN_PAGE_SHIFT + +#define DUMP_PAGE_SHIFT DUMP_MIN_PAGE_SHIFT +#define DUMP_PAGE_SIZE DUMP_MIN_PAGE_SIZE +#define DUMP_PAGE_MASK DUMP_MIN_PAGE_MASK +#define DUMP_PAGE_ALIGN(addr) DUMP_MIN_PAGE_ALIGN(addr) + +struct kl_ia64_fpreg { + union { + unsigned long bits[2]; + long double __dummy; /* force 16-byte alignment */ + } u; +}; + +struct kl_pt_regs_ia64 { + /* for 2.6 kernels only. This structure was totally different in 2.4 kernels */ + unsigned long b6; /* scratch */ + unsigned long b7; /* scratch */ + + unsigned long ar_csd; /* used by cmp8xchg16 (scratch) */ + unsigned long ar_ssd; /* reserved for future use (scratch) */ + + unsigned long r8; /* scratch (return value register 0) */ + unsigned long r9; /* scratch (return value register 1) */ + unsigned long r10; /* scratch (return value register 2) */ + unsigned long r11; /* scratch (return value register 3) */ + + unsigned long cr_ipsr; /* interrupted task's psr */ + unsigned long cr_iip; /* interrupted task's instruction pointer */ + unsigned long cr_ifs; /* interrupted task's function state */ + + unsigned long ar_unat; /* interrupted task's NaT register (preserved) */ + unsigned long ar_pfs; /* prev function state */ + unsigned long ar_rsc; /* RSE configuration */ + /* The following two are valid only if cr_ipsr.cpl > 0: */ + unsigned long ar_rnat; /* RSE NaT */ + unsigned long ar_bspstore; /* RSE bspstore */ + + unsigned long pr; /* 64 predicate registers (1 bit each) */ + unsigned long b0; /* return pointer (bp) */ + unsigned long loadrs; /* size of dirty partition << 16 */ + + unsigned long r1; /* the gp pointer */ + unsigned long r12; /* interrupted task's memory stack pointer */ + unsigned long r13; /* thread pointer */ + + unsigned long ar_fpsr; /* floating point status (preserved) */ + unsigned long r15; /* scratch */ + + /* The remaining registers are NOT saved for system calls. */ + + unsigned long r14; /* scratch */ + unsigned long r2; /* scratch */ + unsigned long r3; /* scratch */ + + /* The following registers are saved by SAVE_REST: */ + unsigned long r16; /* scratch */ + unsigned long r17; /* scratch */ + unsigned long r18; /* scratch */ + unsigned long r19; /* scratch */ + unsigned long r20; /* scratch */ + unsigned long r21; /* scratch */ + unsigned long r22; /* scratch */ + unsigned long r23; /* scratch */ + unsigned long r24; /* scratch */ + unsigned long r25; /* scratch */ + unsigned long r26; /* scratch */ + unsigned long r27; /* scratch */ + unsigned long r28; /* scratch */ + unsigned long r29; /* scratch */ + unsigned long r30; /* scratch */ + unsigned long r31; /* scratch */ + + unsigned long ar_ccv; /* compare/exchange value (scratch) */ + + /* + * * Floating point registers that the kernel considers scratch: + * */ + struct kl_ia64_fpreg f6; /* scratch */ + struct kl_ia64_fpreg f7; /* scratch */ + struct kl_ia64_fpreg f8; /* scratch */ + struct kl_ia64_fpreg f9; /* scratch */ + struct kl_ia64_fpreg f10; /* scratch */ + struct kl_ia64_fpreg f11; /* scratch */ +} __attribute__((packed)); + +/* + * Structure: dump_header_asm_t + * Function: This is the header for architecture-specific stuff. It + * follows right after the dump header. + */ +typedef struct kl_dump_header_ia64_s { + /* the dump magic number -- unique to verify dump is valid */ + uint64_t magic_number; + /* the version number of this dump */ + uint32_t version; + /* the size of this header (in case we can't read it) */ + uint32_t header_size; + /* pointer to pt_regs */ + uint64_t pt_regs; + /* the dump registers */ + struct kl_pt_regs_ia64 regs; + /* the rnat register saved after flushrs */ + uint64_t rnat; + /* the pfs register saved after flushrs */ + uint64_t pfs; + /* the bspstore register saved after flushrs */ + uint64_t bspstore; + + /* smp specific */ + uint32_t smp_num_cpus; + uint32_t dumping_cpu; + struct kl_pt_regs_ia64 smp_regs[KL_NR_CPUS]; + uint64_t smp_current_task[KL_NR_CPUS]; + uint64_t stack[KL_NR_CPUS]; +} __attribute__((packed)) kl_dump_header_ia64_t; + +/* The following struct is used just to calculate the size needed + * to store per CPU info. (Make sure it is sync with the above struct) + */ +struct kl_dump_CPU_info_ia64 { + struct kl_pt_regs_ia64 smp_regs; + uint64_t smp_current_task; + uint64_t stack; +} __attribute__((packed)); + +/* function declarations + */ +int kl_set_dumparch_ia64(void); +uint32_t dha_num_cpus_ia64(void); +kaddr_t dha_current_task_ia64(int cpuid); +int dha_cpuid_ia64(kaddr_t); +kaddr_t dha_stack_ia64(int); +kaddr_t dha_stack_ptr_ia64(int); +int kl_read_dump_header_ia64(void); + +#endif /* __KL_DUMP_IA64_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_dwarfs.h @@ -0,0 +1,27 @@ +/* + * $Id: kl_dwarfs.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by: Prashanth Tamraparni (prasht@in.ibm.com) + * Contributions by SGI + * + * Copyright (C) 2004 International Business Machines Corp. + * Copyright (C) 2004 Silicon Graphics, Inc. All rights reserved. + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ +#ifndef __KL_DWARFS_H +#define __KL_DWARFS_H + +/* Dwarf function declarations */ + +int dw_open_namelist(char*, int); +int dw_setup_typeinfo(void); + +#endif /* __KL_DWARFS_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_error.h @@ -0,0 +1,266 @@ +/* + * $Id: kl_error.h 1169 2005-03-02 21:38:01Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_ERROR_H +#define __KL_ERROR_H + +extern uint64_t klib_error; +extern FILE *kl_stdout; +extern FILE *kl_stderr; + +/* Error Classes + */ +#define KLEC_APP 0 +#define KLEC_KLIB 1 +#define KLEC_MEM 2 +#define KLEC_SYM 3 +#define KLEC_KERN 4 + +#define KLEC_CLASS_MASK 0x00000000ff000000ULL +#define KLEC_CLASS_SHIFT 24 +#define KLEC_ECODE_MASK 0x0000000000ffffffULL +#define KLEC_TYPE_MASK 0xffffffff00000000ULL +#define KLEC_TYPE_SHIFT 32 +#define KLEC_CLASS(e) ((e & KLEC_CLASS_MASK) >> KLEC_CLASS_SHIFT) +#define KLEC_ECODE(e) (e & KLEC_ECODE_MASK) +#define KLEC_TYPE(e) ((e & KLEC_TYPE_MASK) >> KLEC_TYPE_SHIFT) + +void kl_reset_error(void); /* reset klib_error */ +void kl_print_error(void); /* print warning/error messages */ +void kl_check_error(char*); /* check for/handle errors, generate messages */ + +/* FIXME: not used yet -- for changes in future, improve error handling + */ +typedef struct klib_error_s{ + uint32_t code; /* error code */ + uint16_t class; /* error class */ + uint16_t severity; /* severity of error: e.g. warning or fatal error */ + uint32_t datadesc; /* description of data which caused the error */ + FILE *fp; /* fp where to place warning and error messages */ +} klib_error_t; + +/* + * Some macros for accessing data in klib_error + */ +#define KL_ERROR klib_error +#define KL_ERRORFP kl_stderr + +/* Error codes + * + * There are basically two types of error codes -- with each type + * residing in a single word in a two word error code value. The lower + * 32-bits contains an error class and code that represents exactly + * WHAT error occurred (e.g., non-numeric text in a numeric value + * entered by a user, bad virtual address, etc.). + * + * The upper 32-bits represents what type of data was being referenced + * when the error occurred (e.g., bad proc struct). Having two tiers of + * error codes makes it easier to generate useful and specific error + * messages. Note that is possible to have situations where one or the + * other type of error codes is not set. This is OK as long as at least + * one type s set. + */ + +/* General klib error codes + */ +#define KLE_KLIB (KLEC_KLIB << KLEC_CLASS_SHIFT) +#define KLE_NO_MEMORY (KLE_KLIB|1) +#define KLE_OPEN_ERROR (KLE_KLIB|2) +#define KLE_ZERO_BLOCK (KLE_KLIB|3) +#define KLE_INVALID_VALUE (KLE_KLIB|4) +#define KLE_NULL_BUFF (KLE_KLIB|5) +#define KLE_ZERO_SIZE (KLE_KLIB|6) +#define KLE_ACTIVE (KLE_KLIB|7) +#define KLE_NULL_POINTER (KLE_KLIB|8) +#define KLE_UNSUPPORTED_ARCH (KLE_KLIB|9) + +#define KLE_MISC_ERROR (KLE_KLIB|97) +#define KLE_NOT_SUPPORTED (KLE_KLIB|98) +#define KLE_UNKNOWN_ERROR (KLE_KLIB|99) + +/* memory error codes + */ +#define KLE_MEM (KLEC_MEM << KLEC_CLASS_SHIFT) +#define KLE_BAD_MAP_FILE (KLE_MEM|1) +#define KLE_BAD_DUMP (KLE_MEM|2) +#define KLE_BAD_DUMPTYPE (KLE_MEM|3) +#define KLE_INVALID_LSEEK (KLE_MEM|4) +#define KLE_INVALID_READ (KLE_MEM|5) +#define KLE_BAD_KERNINFO (KLE_MEM|6) +#define KLE_INVALID_PADDR (KLE_MEM|7) +#define KLE_INVALID_VADDR (KLE_MEM|8) +#define KLE_INVALID_VADDR_ALIGN (KLE_MEM|9) +#define KLE_INVALID_MAPPING (KLE_MEM|10) +#define KLE_CMP_ERROR (KLE_MEM|11) +#define KLE_INVALID_DUMP_MAGIC (KLE_MEM|12) +#define KLE_KERNEL_MAGIC_MISMATCH (KLE_MEM|13) +#define KLE_NO_END_SYMBOL (KLE_MEM|14) +#define KLE_INVALID_DUMP_HEADER (KLE_MEM|15) +#define KLE_DUMP_INDEX_CREATION (KLE_MEM|16) +#define KLE_DUMP_HEADER_ONLY (KLE_MEM|17) +#define KLE_PAGE_NOT_PRESENT (KLE_MEM|18) +#define KLE_BAD_ELF_FILE (KLE_MEM|19) +#define KLE_ARCHIVE_FILE (KLE_MEM|20) +#define KLE_MAP_FILE_PRESENT (KLE_MEM|21) +#define KLE_BAD_MAP_FILENAME (KLE_MEM|22) +#define KLE_BAD_DUMP_FILENAME (KLE_MEM|23) +#define KLE_BAD_NAMELIST_FILE (KLE_MEM|24) +#define KLE_BAD_NAMELIST_FILENAME (KLE_MEM|25) +#define KLE_LIVE_SYSTEM (KLE_MEM|26) +#define KLE_NOT_INITIALIZED (KLE_MEM|27) + +/* symbol error codes + */ +#define KLE_SYM (KLEC_SYM << KLEC_CLASS_SHIFT) +#define KLE_NO_SYMTAB (KLE_SYM|1) +#define KLE_NO_SYMBOLS (KLE_SYM|2) +#define KLE_INVALID_TYPE (KLE_SYM|3) +#define KLE_NO_MODULE_LIST (KLE_SYM|4) + +/* kernel data error codes + */ +#define KLE_KERN (KLEC_KERN << KLEC_CLASS_SHIFT) +#define KLE_INVALID_KERNELSTACK (KLE_KERN|1) +#define KLE_INVALID_STRUCT_SIZE (KLE_KERN|2) +#define KLE_BEFORE_RAM_OFFSET (KLE_KERN|3) +#define KLE_AFTER_MAXPFN (KLE_KERN|4) +#define KLE_AFTER_PHYSMEM (KLE_KERN|5) +#define KLE_AFTER_MAXMEM (KLE_KERN|6) +#define KLE_PHYSMEM_NOT_INSTALLED (KLE_KERN|7) +#define KLE_NO_DEFTASK (KLE_KERN|8) +#define KLE_PID_NOT_FOUND (KLE_KERN|9) +#define KLE_DEFTASK_NOT_ON_CPU (KLE_KERN|10) +#define KLE_NO_CURCPU (KLE_KERN|11) +#define KLE_NO_CPU (KLE_KERN|12) +#define KLE_SIG_ERROR (KLE_KERN|13) +#define KLE_TASK_RUNNING (KLE_KERN|14) +#define KLE_NO_SWITCH_STACK (KLE_KERN|15) + +/* Error codes that indicate what type of data was bad. These are + * placed in the upper 32-bits of klib_error. + */ +#define KLE_BAD_TASK_STRUCT (((uint64_t)1)<<32) +#define KLE_BAD_SYMNAME (((uint64_t)2)<<32) +#define KLE_BAD_SYMADDR (((uint64_t)3)<<32) +#define KLE_BAD_FUNCADDR (((uint64_t)4)<<32) +#define KLE_BAD_STRUCT (((uint64_t)5)<<32) +#define KLE_BAD_FIELD (((uint64_t)6)<<32) +#define KLE_BAD_PC (((uint64_t)7)<<32) +#define KLE_BAD_RA (((uint64_t)8)<<32) +#define KLE_BAD_SP (((uint64_t)9)<<32) +#define KLE_BAD_EP (((uint64_t)10)<<32) +#define KLE_BAD_SADDR (((uint64_t)11)<<32) +#define KLE_BAD_KERNELSTACK (((uint64_t)12)<<32) +#define KLE_BAD_LINENO (((uint64_t)13)<<32) +#define KLE_MAP_FILE (((uint64_t)14)<<32) +#define KLE_DUMP (((uint64_t)15)<<32) +#define KLE_BAD_STRING (((uint64_t)16)<<32) +#define KLE_ELF_FILE (((uint64_t)17)<<32) + +/* flags for function kl_msg() + * First 3 bits define trace levels. Minimum trace threshold is trace level 1. + * So maximal 7 trace levels are possible. We are using only KLE_TRACELEVEL_MAX. + * If no trace level bits are set, it is normal output. + */ +#define _KLE_TRACEBIT1 0x00000001 /* trace bit 1 */ +#define _KLE_TRACEBIT2 0x00000002 /* trace bit 2 */ +#define _KLE_TRACEBIT3 0x00000004 /* trace bit 3 */ +#define _KLE_TRACENUM 8 /* used in _KLE_TRACENUM */ +#define _KLE_TRACEMASK (_KLE_TRACENUM-1) /* mask for trace bits */ +/* further flags */ +#define KLE_F_NOORIGIN 0x00001000 /* do not print origin for this msg */ +#define KLE_F_ERRORMSG 0x00002000 /* treat message as error message */ +/* trace levels := predefined combinations of trace bits */ +#define KLE_F_TRACELEVEL1 (_KLE_TRACEBIT1) +#define KLE_F_TRACELEVEL2 (_KLE_TRACEBIT2) +#define KLE_F_TRACELEVEL3 (_KLE_TRACEBIT1|_KLE_TRACEBIT2) +#define KLE_F_TRACELEVEL4 (_KLE_TRACEBIT3) +#define KLE_TRACELEVELMAX 4 +#define KLE_TRACELEVEL(flg) (flg & _KLE_TRACEMASK) +#define KLE_GETTRACELEVEL(flg) \ + ((KLE_TRACELEVEL(flg) > KLE_TRACELEVELMAX) ? KLE_TRACELEVELMAX : \ + KLE_TRACELEVEL(flg)) + +/* define debug components of libklib (64 components possible) + * used by kl_msg() + */ +#define KL_DBGCOMP_ALLOC 0x0000000001 /* liballoc */ +#define KL_DBGCOMP_BFD 0x0000000002 /* general bfd support */ +#define KL_DBGCOMP_BTREE 0x0000000004 /* btree implementation */ +#define KL_DBGCOMP_COMPRESS 0x0000000008 /* gzip/rle (de)compression */ +#define KL_DBGCOMP_INIT 0x0000000010 /* klib initialization */ +#define KL_DBGCOMP_MEMMAP 0x0000000020 /* memory mapping */ +#define KL_DBGCOMP_MODULE 0x0000000040 /* kernel module handling */ +#define KL_DBGCOMP_SIGNAL 0x0000000080 /* signal handling */ +#define KL_DBGCOMP_STABS 0x0000000100 /* stabs format support */ +#define KL_DBGCOMP_SYMBOL 0x0000000200 /* symbol handling */ +#define KL_DBGCOMP_TYPE 0x0000000400 /* type information handling */ +#define KL_DBGCOMP_ALL ((uint64_t) -1) /* all components */ + +/* central output routine, shouldn't be used directly, but + * by following macros + */ +void kl_msg(uint64_t, uint32_t, const char*, const char*, int, + const char*, ...); + +/* vararg macros that should be used instead of kl_msg() + */ +/* used within libklib to print non-error messages (e.g. progress indication) + */ +#define KL_MSG(fmt, args...) \ +kl_msg(0, 0, NULL, NULL, 0, fmt, ## args) +/* Can be used by application to print error messages; + * not used by libklib itself. + */ +#define kl_error(fmt, args...) \ +kl_msg(0, KLE_F_ERRORMSG, __FUNCTION__, __FILE__, __LINE__, fmt, ## args) +/* Generate trace messages. Used for libklib debugging. Might be used + * by an application, too. + * A macro _DBG_COMPONENT has to be defined locally in the module where + * any trace macro is used. See above debug components. + * Trace messages are only printed iff _DBG_COMPONENT was set before with a + * call to kl_set_dbg_component(). + */ +#define kl_trace1(flg, fmt, args...) \ +kl_msg(_DBG_COMPONENT, KLE_F_TRACELEVEL1|(flg), \ + __FUNCTION__, __FILE__, __LINE__, fmt, ## args) +#define kl_trace2(flg, fmt, args...) \ +kl_msg(_DBG_COMPONENT, KLE_F_TRACELEVEL2|(flg), \ + __FUNCTION__, __FILE__, __LINE__, fmt, ## args) +#define kl_trace3(flg, fmt, args...) \ +kl_msg(_DBG_COMPONENT, KLE_F_TRACELEVEL3|(flg), \ + __FUNCTION__, __FILE__, __LINE__, fmt, ## args) +#define kl_trace4(flg, fmt, args...) \ +kl_msg(_DBG_COMPONENT, KLE_F_TRACELEVEL4|(flg), \ + __FUNCTION__, __FILE__, __LINE__, fmt, ## args) + +/* functions to set some global variables for libklib debugging + */ +int kl_set_trace_threshold(uint32_t); +void kl_set_dbg_component(uint64_t); +void kl_set_stdout(FILE *); +void kl_set_stderr(FILE *); + +/* functions to get contents of global variables for libklib debugging + */ +uint32_t kl_get_trace_threshold(void); +uint64_t kl_get_dbg_component(void); + +#endif /* __KL_ERROR_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_htnode.h @@ -0,0 +1,71 @@ +/* + * $Id: kl_htnode.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libutil. + * A library which provides auxiliary functions. + * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_HTNODE_H +#define __KL_HTNODE_H + +/* Node structure for use in hierarchical trees (htrees). + */ +typedef struct htnode_s { + struct htnode_s *next; + struct htnode_s *prev; + struct htnode_s *parent; + struct htnode_s *children; + int seq; + int level; + int key; +} htnode_t; + +/* Flag values + */ +#define HT_BEFORE 0x1 +#define HT_AFTER 0x2 +#define HT_CHILD 0x4 +#define HT_PEER 0x8 + +/* Function prototypes + */ +htnode_t *kl_next_htnode( + htnode_t * /* htnode pointer */); + +htnode_t *kl_prev_htnode( + htnode_t * /* htnode pointer */); + +void ht_insert_peer( + htnode_t * /* htnode pointer */, + htnode_t * /* new htnode pointer*/, + int /* flags */); + +void ht_insert_child( + htnode_t * /* htnode pointer */, + htnode_t * /* new htnode pointer*/, + int /* flags */); + +int ht_insert( + htnode_t * /* htnode pointer */, + htnode_t * /* new htnode pointer*/, + int /* flags */); + +void ht_insert_next_htnode( + htnode_t * /* htnode pointer */, + htnode_t * /* new htnode pointer*/); + +#endif /* __KL_HTNODE_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_lib.h @@ -0,0 +1,65 @@ +/* + * $Id: kl_lib.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libutil. + * A library which provides auxiliary functions. + * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_LIB_H +#define __KL_LIB_H + +/* Include system header files + */ + +#if 0 +/* cpw: we don't need this userland stuff: */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +/* Include lkcd library header files + */ +/* cpw: change these from the < > form to the " " form: */ +#include "kl_types.h" +#include "kl_alloc.h" +#include "kl_libutil.h" +#include "kl_btnode.h" +#include "kl_htnode.h" +#include "kl_queue.h" +#include "kl_stringtab.h" + +#endif /* __KL_LIB_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_libutil.h @@ -0,0 +1,40 @@ +/* + * $Id: kl_libutil.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libutil. + * A library which provides auxiliary functions. + * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2004 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_LIBUTIL_H +#define __KL_LIBUTIL_H + +/* cpw: change all these from the < > form to the " " form: */ +#include "kl_alloc.h" +#include "kl_btnode.h" +#include "kl_copt.h" +#include "kl_htnode.h" +#include "kl_queue.h" +#include "kl_stringtab.h" + +int kl_shift_value(uint64_t ); +int kl_string_compare(char *, char *); +int kl_string_match(char *, char *); +uint64_t kl_strtoull(char *, char **, int); +time_t kl_str_to_ctime(char *); +void *kl_get_ra(void); + +#endif /* __KL_LIBUTIL_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_mem.h @@ -0,0 +1,104 @@ +/* + * $Id: kl_mem.h 1157 2005-02-25 22:04:05Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_MEM_H +#define __KL_MEM_H + +#if 0 +cpw: skip: +extern kaddr_t VMALLOC_START; +extern kaddr_t VMALLOC_END; +#endif + +/* + * Function prototypes + */ + +int kl_linux_release(void); + +k_error_t kl_readmem( + kaddr_t /* physical address to start reading from */, + unsigned /* number of bytes to read */, + void * /* pointer to buffer */); + +k_error_t kl_readkmem( + kaddr_t /* virtual address to start reading from */, + unsigned /* number of bytes to read */, + void * /* pointer to buffer */); + +int kl_virtop( + kaddr_t /* virtual address to translate */, + void * /* pointer to mem_map for address translation */, + kaddr_t * /* pointer to physical address to return */); + +k_error_t kl_get_block( + kaddr_t /* virtual address */, + unsigned /* size of block to read in */, + void * /* pointer to buffer */, + void * /* pointer to mmap */); + +/* Wrapper that eliminates the mmap parameter + */ +#define GET_BLOCK(a, s, b) kl_get_block(a, s, (void *)b, (void *)0) + +uint64_t kl_uint( + void * /* pointer to buffer containing struct */, + char * /* name of struct */, + char * /* name of member */, + unsigned /* offset */); + +int64_t kl_int( + void * /* pointer to buffer containing struct */, + char * /* name of struct */, + char * /* name of member */, + unsigned /* offset */); + +kaddr_t kl_kaddr( + void * /* pointer to buffer containing struct */, + char * /* name of struct */, + char * /* name of member */); + +/* XXX deprecated use KL_READ_PTR() instead */ +kaddr_t kl_kaddr_to_ptr( + kaddr_t /* Address to dereference */); + +int kl_is_valid_kaddr( + kaddr_t /* Address to test */, + void * /* pointer to mmap */, + int /* flags */); + +/* REMIND: + * Likely not right for ia64 + */ +#define KL_KADDR_IS_PHYSICAL(vaddr) ((vaddr >= KL_PAGE_OFFSET) && \ + (vaddr <= KL_HIGH_MEMORY)) + +#define PGNO_TO_PADDR(pgno) (pgno << KL_PAGE_SHIFT) + +/* + * declaration of some defaults that are used in kl_set_dumparch() + */ +int kl_valid_physaddr(kaddr_t); +int kl_valid_physmem(kaddr_t, int); +kaddr_t kl_next_valid_physaddr(kaddr_t); +kaddr_t kl_fix_vaddr(kaddr_t, size_t); +int kl_init_virtop(void); + +#endif /* __KL_MEM_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_mem_ia64.h @@ -0,0 +1,149 @@ +/* + * $Id: kl_mem_ia64.h 1250 2006-04-18 18:23:44Z cliffpwickman $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_MEM_IA64_H +#define __KL_MEM_IA64_H + +/* XXX - the following macros are used by functions in kl_page.c and in */ +/* function kl_virtop, they still have to be defined properly, */ +/* all the following macros have first to be set with correct values. */ +/* I don't have a clue what values to use for ia64 architecture!!! */ + +/* KSTACK_SIZE depends on page size (see kernel headers ptrace.h and page.h) + * #define IA64_STK_OFFSET ((1 << IA64_TASK_STRUCT_LOG_NUM_PAGES)*PAGE_SIZE) + * and + * #define PAGE_SIZE 1UL<= KL_HIGH_MEMORY))) + +uint32_t dha_num_cpus_ia64(void); +kaddr_t dha_current_task_ia64(int); +int dha_cpuid_ia64(kaddr_t); +kaddr_t dha_stack_ia64(int); +kaddr_t dha_stack_ptr_ia64(int); +kaddr_t kl_kernelstack_ia64(kaddr_t); +kaddr_t kl_mmap_virtop_ia64(kaddr_t, void*); +int kl_init_virtop_ia64(void); +int kl_virtop_ia64(kaddr_t, void*, kaddr_t*); +int kl_vtop_ia64(kaddr_t, kaddr_t*); +int kl_valid_physmem_ia64(kaddr_t, int); +kaddr_t kl_next_valid_physaddr_ia64(kaddr_t); +kaddr_t kl_fix_vaddr_ia64(kaddr_t, size_t); + +/* Structure containing key data for ia64 virtual memory mapping. + * Note that a number of fields are SN system specific. + */ +typedef struct ia64_vminfo_s { + int flags; + kaddr_t vpernode_base; + kaddr_t vglobal_base; + kaddr_t to_phys_mask; + kaddr_t kernphysbase; + int nasid_shift; /* SN specific */ + int nasid_mask; /* SN specific */ +} ia64_vminfo_t; + +extern ia64_vminfo_t ia64_vminfo; + +/* Some vminfo flags + */ +#define MAPPED_KERN_FLAG 0x1 +#define SN2_FLAG 0x2 + +/* Some vminfo macros + */ +#define IS_MAPPED_KERN (ia64_vminfo.flags & MAPPED_KERN_FLAG) +#define IS_SN2 (ia64_vminfo.flags & SN2_FLAG) +#define KL_VPERNODE_BASE ia64_vminfo.vpernode_base +#define KL_VGLOBAL_BASE ia64_vminfo.vglobal_base +#define KL_TO_PHYS_MASK ia64_vminfo.to_phys_mask +#define KL_KERNPHYSBASE ia64_vminfo.kernphysbase +#define KL_NASID_SHIFT ia64_vminfo.nasid_shift +#define KL_NASID_MASK ia64_vminfo.nasid_mask + +#define ADDR_TO_NASID(A) (((A) >> (long)(KL_NASID_SHIFT)) & KL_NASID_MASK) + +#endif /* __KL_MEM_IA64_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_module.h @@ -0,0 +1,69 @@ +/* + * $Id: kl_module.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_MODULE_H +#define __KL_MODULE_H + +/* + * insmod generates ksymoops + * + */ + +typedef struct kl_modinfo_s { + char *modname; /* name of module as loaded in dump */ + /* store ksym info for all modules in a linked list */ + struct kl_modinfo_s *next; + char *object_file; /* name of file that module was loaded from*/ + /* ? possibly store modtime and version here ? */ + uint64_t header; /* address of module header */ + uint64_t mtime; /* time of last modification of object_file */ + uint32_t version; /* kernel version that module was compiled for */ + uint64_t text_sec; /* address of text section */ + uint64_t text_len; /* length of text section */ + uint64_t data_sec; /* address of data section */ + uint64_t data_len; /* length of data section */ + uint64_t rodata_sec; /* address of rodata section */ + uint64_t rodata_len; /* length of rodata section */ + uint64_t bss_sec; /* address of rodata section */ + uint64_t bss_len; /* length of rodata section */ + char *ksym_object; /* ksym for object */ + char *ksym_text_sec; /* ksym for its text section */ + char *ksym_data_sec; /* ksym for its data section */ + char *ksym_rodata_sec; /* ksym for its rodata section */ + char *ksym_bss_sec; /* ksym for its bss sectio */ +} kl_modinfo_t; + +int kl_get_module(char*, kaddr_t*, void**); +int kl_get_module_2_6(char*, kaddr_t*, void**); +int kl_get_modname(char**, void*); +int kl_new_get_modname(char**, void*); +void kl_free_modinfo(kl_modinfo_t**); +int kl_new_modinfo(kl_modinfo_t**, void*); +int kl_set_modinfo(kaddr_t, char*, kl_modinfo_t*); +int kl_complete_modinfo(kl_modinfo_t*); +int kl_load_ksyms(int); +int kl_load_ksyms_2_6(int); +int kl_unload_ksyms(void); +int kl_load_module_sym(char*, char*, char*); +int kl_unload_module_sym(char*); +int kl_autoload_module_info(char*); +kl_modinfo_t * kl_lkup_modinfo(char*); + +#endif /* __KL_MODULE_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_queue.h @@ -0,0 +1,89 @@ +/* + * $Id: kl_queue.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libutil. + * A library which provides auxiliary functions. + * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_QUEUE_H +#define __KL_QUEUE_H + +/* List element header + */ +typedef struct element_s { + struct element_s *next; + struct element_s *prev; +} element_t; + +/* Some useful macros + */ +#define ENQUEUE(list, elem) \ + kl_enqueue((element_t **)list, (element_t *)elem) +#define DEQUEUE(list) kl_dequeue((element_t **)list) +#define FINDQUEUE(list, elem) \ + kl_findqueue((element_t **)list, (element_t *)elem) +#define REMQUEUE(list, elem) kl_remqueue((element_t **)list, (element_t *)elem) + +typedef struct list_of_ptrs { + element_t elem; + unsigned long long val64; +} list_of_ptrs_t; + +#define FINDLIST_QUEUE(list, elem, compare) \ + kl_findlist_queue((list_of_ptrs_t **)list, \ + (list_of_ptrs_t *)elem, compare) + +/** + ** Function prototypes + **/ + +/* Add a new element to the tail of a doubly linked list. + */ +void kl_enqueue( + element_t** /* ptr to head of list */, + element_t* /* ptr to element to add to the list */); + +/* Remove an element from the head of a doubly linked list. A pointer + * to the element will be returned. In the event that the list is + * empty, a NULL pointer will be returned. + */ +element_t *kl_dequeue( + element_t** /* ptr to list head (first item removed) */); + +/* Checks to see if a particular element is in a list. If it is, a + * value of one (1) will be returned. Otherwise, a value of zero (0) + * will be returned. + */ +int kl_findqueue( + element_t** /* ptr to head of list */, + element_t* /* ptr to element to find on list */); + +/* Walks through a list of pointers to queues and looks for a + * particular list. + */ +int kl_findlist_queue( + list_of_ptrs_t** /* ptr to list of lists */, + list_of_ptrs_t* /* ptr to list to look for */, + int(*)(void *, void *) /* ptr to compare function */); + +/* Remove specified element from doubly linked list. + */ +void kl_remqueue( + element_t** /* ptr to head of list */, + element_t* /* ptr to element to remove from list */); + +#endif /* __KL_QUEUE_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_stabs.h @@ -0,0 +1,122 @@ +/* + * $Id: kl_stabs.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2004 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_STABS_H +#define __KL_STABS_H + +/* STABS specific types + */ +#define STAB_XSTRUCT 100 /* Cross referense to STAB_STRUCT */ +#define STAB_XUNION 101 /* Cross referense to STAB_UNIONB */ +#define STAB_XENUM 102 /* Cross referense to STAB_ENUM */ + +/* Structure allocated for every namelist. A namelist can be either an + * object file (.o or executible), or it can be an archive (.a). + */ +typedef struct st_nmlist_s { + char *sts_filename; /* disk file name */ + short sts_type; /* ST_OBJ or ST_AR */ + short sts_nfiles; /* number of source/object files */ +} st_nmlist_t; + +/* Values for type field + */ +#define ST_OBJ 1 /* object file (.o or executible) */ +#define ST_AR 2 /* archive */ + +/* Stab entry type Flags. For determining which stab entries to + * capture from the symbol table. + */ +#define ST_UNDF 0x0001 +#define ST_SO 0x0002 +#define ST_LSYM 0x0004 +#define ST_GSYM 0x0008 +#define ST_PSYM 0x0010 +#define ST_STSYM 0x0020 +#define ST_LCSYM 0x0040 +#define ST_FUN 0x0080 +#define ST_BINCL 0x0100 +#define ST_EINCL 0x0200 +#define ST_EXCL 0x0400 +#define ST_SLINE 0x0800 +#define ST_RSYM 0x2000 +#define ST_ALL 0xffff +#define ST_DEFAULT (ST_LSYM|ST_GSYM|ST_FUN) + +#define N_UNDF 0 + +/* Structures that allow us to selectively cycle through only those BFD + * sections containing STAB data. + */ +typedef struct stab_sect_s { + char *stabsect_name; + char *strsect_name; +} stab_sect_t; + +/* Local structure that contains the current type string (which may be + * just a part of the complete type defenition string) and the character + * index (current) pointer. + */ +typedef struct stab_str_s { + char *str; + char *ptr; +} stab_str_t; + +/* Local structure containing global values that allow us to cycle + * through multiple object files without reinitializing. + */ +typedef struct st_global_s { + bfd *abfd; /* current bfd pointer */ + int type; /* symbol entry type */ + int flags; /* want flags */ + int flag; /* current ST flag */ + int nmlist; /* current namelist index */ + int srcfile; /* current source file number */ + int incfile; /* current include file */ + int symnum; /* symbol entry number */ + bfd_byte *stabp; /* beg of current string table */ + bfd_byte *stabs_end; /* end of current string table */ + int staboff; /* current stab table offset */ + unsigned int value; /* value (e.g., function addr) */ + int stroffset; /* offset in stab string table */ + short desc; /* desc value (e.g, line number) */ + stab_str_t stab_str; /* current stab string */ +} st_global_t; + +/* Macros for accessing the current global values + */ +#define G_abfd G_values.abfd +#define G_type G_values.type +#define G_flags G_values.flags +#define G_flag G_values.flag +#define G_nmlist G_values.nmlist +#define G_srcfile G_values.srcfile +#define G_incfile G_values.incfile +#define G_symnum G_values.symnum +#define G_stabp G_values.stabp +#define G_stabs_end G_values.stabs_end +#define G_staboff G_values.staboff +#define G_value G_values.value +#define G_stroffset G_values.stroffset +#define G_desc G_values.desc +#define G_stab_str G_values.stab_str +#define CUR_CHAR G_stab_str.ptr + +#endif /* __KL_STABS_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_stringtab.h @@ -0,0 +1,68 @@ +/* + * $Id: kl_stringtab.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libutil. + * A library which provides auxiliary functions. + * libutil is part of lkcdutils -- utilities for Linux kernel crash dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_STRINGTAB_H +#define __KL_STRINGTAB_H + +/* The string table structure + * + * String space is allocated from 4K blocks which are allocated + * as needed. The first four bytes of each block are reserved so + * that the blocks can be chained together (to make it easy to free + * them when the string table is no longer necessary). + */ +typedef struct string_table_s { + int num_strings; + void *block_list; +} string_table_t; + +#define NO_STRINGTAB 0 +#define USE_STRINGTAB 1 + +/** + ** Function prototypes + **/ + +/* Initialize a string table. Depending on the value of the flag + * parameter, either temporary or permenent blocks will be used. + * Upon success, a pointer to a string table will be returned. + * Otherwise, a NULL pointer will be returned. + */ +string_table_t *kl_init_string_table( + int /* flag (K_TEMP/K_PERM)*/); + +/* Free all memory blocks allocated for a particular string table + * and then free the table itself. + */ +void kl_free_string_table( + string_table_t* /* pointer to string table */); + +/* Search for a string in a string table. If the string does not + * exist, allocate space from the string table and add the string. + * In either event, a pointer to the string (from the table) will + * be returned. + */ +char *kl_get_string( + string_table_t* /* pointer to string table */, + char* /* string to get/add from/to string table */, + int /* flag (K_TEMP/K_PERM)*/); + +#endif /* __KL_STRINGTAB_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_sym.h @@ -0,0 +1,131 @@ +/* + * $Id: kl_sym.h 1233 2005-09-10 08:01:11Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_SYM_H +#define __KL_SYM_H + +/* The syment struct contains information about kernel symbols (text, + * data, etc.). The first field in syment_t is a btnode_s sruct. This + * allows the generic binary search tree routines, insert_tnode() and + * find_tnode(), to be used. + */ +typedef struct syment_s { + btnode_t s_bt; /* Must be first */ + struct syment_s *s_next; /* For linked lists */ + struct syment_s *s_prev; /* For linked lists */ + kaddr_t s_addr; /* vaddr of symbol */ + kaddr_t s_end; /* end address of symbol */ + int s_type; /* text, data */ + struct syment_s *s_forward; /* For linked lists */ +} syment_t; + +#define s_name s_bt.bt_key + +#define SYM_GLOBAL_TEXT 1 +#define SYM_LOCAL_TEXT 2 +#define SYM_LOCORE_TEXT 3 +#define SYM_GLOBAL_DATA 4 +#define SYM_LOCAL_DATA 5 +#define SYM_ABS 6 +#define SYM_UNK 9 +#define SYM_KSYM 10 +#define SYM_KSYM_TEXT 11 +#define SYM_KALLSYMS 12 + +#define SYM_MAP_ANY 0 +#define SYM_MAP_FILE 1 +#define SYM_MAP_KSYM 2 +#define SYM_MAP_MODULE 3 +#define SYM_MAP_KALLSYMS 4 + +#define KL_KERNEL_MODULE "kernel_module" +#define KL_S_BSS ".bss.start" +#define KL_E_BSS ".bss.end" +#define KL_S_DATA ".data.start" +#define KL_E_DATA ".data.end" +#define KL_S_RODATA ".rodata.start" +#define KL_E_RODATA ".rodata.end" +#define KL_S_TEXT ".text.start" +#define KL_E_TEXT ".text.end" +#define KL_SYM_END "__end__" + + +#define KL_SYMBOL_NAME_LEN 256 + +/* + * Struct containing symbol table information + */ +typedef struct symtab_s { + int symcnt; /* Number of symbols */ + int symaddrcnt; /* Number of symbol addrs to track */ + syment_t **symaddrs; /* Table of symbols by address */ + btnode_t *symnames; /* tree of symbols by name */ + syment_t *text_list; /* Linked list of text symbols */ + syment_t *data_list; /* Linked list of data symbols */ +} symtab_t; + + +/* support of further mapfiles besides System.map */ +typedef struct maplist_s { + struct maplist_s *next; + int maplist_type; /* type of maplist */ + char *mapfile; /* name of mapfile */ + char *modname; /* set if map belongs to a module */ + symtab_t *syminfo; +} maplist_t; + + +/* API Function prototypes + */ +int kl_read_syminfo(maplist_t*); +int kl_free_syminfo(char*); +void kl_free_symtab(symtab_t*); +void kl_free_syment_list(syment_t*); +void kl_free_maplist(maplist_t*); +syment_t *kl_get_similar_name(char*, char*, int*, int*); +syment_t *kl_lkup_symname(char*); +syment_t *_kl_lkup_symname(char*, int, size_t len); +#define KL_LKUP_SYMNAME(NAME, TYPE, LEN) _kl_lkup_symname(NAME, TYPE, LEN) +syment_t *kl_lkup_funcaddr(kaddr_t); +syment_t *kl_lkup_symaddr(kaddr_t); +syment_t *kl_lkup_symaddr_text(kaddr_t); +syment_t *_kl_lkup_symaddr(kaddr_t, int); +#define KL_LKUP_SYMADDR(KADDR, TYPE) _kl_lkup_symaddr(KADDR, TYPE) +kaddr_t kl_symaddr(char * /* symbol name */); +kaddr_t kl_symptr(char * /* symbol name */); +kaddr_t kl_funcaddr(kaddr_t /* pc value */); +char *kl_funcname(kaddr_t /* pc value */); +int kl_funcsize(kaddr_t /* pc value */); +int kl_symsize(syment_t*); +syment_t *kl_alloc_syment(kaddr_t, kaddr_t, int, const char*); +void kl_insert_symbols(symtab_t*, syment_t*); +int kl_insert_artificial_symbols(symtab_t*, syment_t**, kl_modinfo_t*); +int kl_convert_symbol(kaddr_t*, int*, char, kl_modinfo_t*); +int kl_load_sym(char*); +int kl_print_symtables(char*, char*, int, int); +void kl_print_symbol(kaddr_t, syment_t*, int); + +/* flag for use by kl_print_symbol() and kl_print_syminfo() + */ +#define KL_SYMWOFFSET (0x01) /* with offset field */ +#define KL_SYMFULL (0x02) /* print detailed syminfo */ +#define KL_SYMBYNAME (0x04) /* print symbol sorted by name */ + +#endif /* __KL_SYM_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_task.h @@ -0,0 +1,39 @@ +/* + * $Id: kl_task.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2002, 2004 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_TASK_H +#define __KL_TASK_H + +extern kaddr_t deftask; + +/* Function prototypes + */ +k_error_t kl_set_deftask(kaddr_t); +int kl_parent_pid(void *); +kaddr_t kl_pid_to_task(kaddr_t); +k_error_t kl_get_task_struct(kaddr_t, int, void *); +kaddr_t kl_kernelstack(kaddr_t); +kaddr_t kl_first_task(void); +kaddr_t kl_next_task(void *); +kaddr_t kl_prev_task(void *); +kaddr_t kl_pid_to_task(kaddr_t); +int kl_task_size(kaddr_t); + +#endif /* __KL_TASK_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_typeinfo.h @@ -0,0 +1,199 @@ +/* + * $Id: kl_typeinfo.h 1259 2006-04-25 18:33:20Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2006 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_TYPEINFO_H +#define __KL_TYPEINFO_H + +#define KLT_BASE 0x001 +#define KLT_STRUCT 0x002 +#define KLT_UNION 0x004 +#define KLT_ENUMERATION 0x008 +#define KLT_MEMBER 0x010 +#define KLT_ARRAY 0x020 +#define KLT_POINTER 0x040 +#define KLT_TYPEDEF 0x080 +#define KLT_FUNCTION 0x100 +#define KLT_VARIABLE 0x200 +#define KLT_SRCFILE 0x400 +#define KLT_SUBRANGE 0x800 +#define KLT_INCOMPLETE 0x4000 +#define KLT_UNKNOWN 0x8000 +#define KLT_TYPE (KLT_BASE|KLT_STRUCT|KLT_UNION|KLT_ENUMERATION) +#define KLT_TYPES (KLT_BASE|KLT_STRUCT|KLT_UNION|KLT_ENUMERATION|KLT_TYPEDEF) + +#define IS_TYPE(T) ((T) & KLT_TYPE) +#define IS_STRUCT(T) ((T) & KLT_STRUCT) +#define IS_UNION(T) ((T) & KLT_UNION) +#define IS_ENUM(T) ((T) & KLT_ENUM) +#define IS_MEMBER(T) ((T) & KLT_MEMBER) +#define IS_POINTER(T) ((T) & KLT_POINTER) +#define IS_TYPEDEF(T) ((T) & KLT_TYPEDEF) + +#define TYP_SETUP_FLG 0x01 +#define TYP_TYPESTRING_FLG 0x02 +#define TYP_INCOMPLETE_FLG 0x04 +#define TYP_XREFERENCE_FLG 0x08 +#define TYP_ANONYMOUS_FLG 0x10 /* Denotes anonymous union or struct */ + +#define NO_INDENT 0x01000000 +#define SUPPRESS_NAME 0x02000000 +#define SUPPRESS_NL 0x04000000 +#define SUPPRESS_SEMI_COLON 0x08000000 +#define NO_REALTYPE 0x10000000 + +extern int numnmlist; + +#define KL_TYPEINFO() (numnmlist) + +typedef struct kltype_s { + char *kl_name; /* type name */ + char *kl_typestr; /* 'typecast' string */ + void *kl_ptr; /* ptr to arch typeinfo */ + int kl_flags; /* (e.g., STAB_FLG) */ + int kl_type; /* (e.g., KLT_TYPEDEF) */ + int kl_offset; /* offset to 1st byte */ + int kl_size; /* number of bytes */ + int kl_bit_offset; /* offset to 1st data bit */ + int kl_bit_size; /* total num of data bits */ + int kl_encoding; /* for base value types */ + int kl_low_bounds; /* for arrays */ + int kl_high_bounds; /* for arrays */ + unsigned int kl_value; /* enum value, etc. */ + struct kltype_s *kl_member; /* struct/union member list */ + struct kltype_s *kl_next; /* hash lists, etc. */ + struct kltype_s *kl_realtype; /* pointer to real type */ + struct kltype_s *kl_indextype; /* pointer to index_type */ + struct kltype_s *kl_elementtype; /* pointer to element_type */ +} kltype_t; + +/* Flag values + */ +#define K_HEX 0x1 +#define K_OCTAL 0x2 +#define K_BINARY 0x4 +#define K_NO_SWAP 0x8 + +/* Base type encoding values + */ +#define ENC_CHAR 0x01 +#define ENC_SIGNED 0x02 +#define ENC_UNSIGNED 0x04 +#define ENC_FLOAT 0x08 +#define ENC_ADDRESS 0x10 +#define ENC_UNDEFINED 0x20 + +/* Maximum number of open namelists + */ +#define MAXNMLIST 10 + +typedef struct nmlist_s { + int index; + char *namelist; + void *private; /* pointer to private control struct */ + string_table_t *stringtab; +} nmlist_t; + +extern nmlist_t nmlist[]; +extern int numnmlist; +extern int curnmlist; + +#define KL_TYPESTR_STRUCT "struct" +#define KL_TYPESTR_UNION "union" +#define KL_TYPESTR_ENUM "enum" +#define KL_TYPESTR_VOID "void" + +/* Function prototypes + */ +kltype_t *kl_find_type( + char * /* type name */, + int /* type number */); + +kltype_t *kl_find_next_type( + kltype_t * /* kltype_t pointer */, + int /* type number */); + +kltype_t *kl_first_type( + int /* type number */); + +kltype_t *kl_next_type( + kltype_t * /* kltype_t pointer */); + +kltype_t *kl_prev_type( + kltype_t * /* kltype_t pointer */); + +kltype_t *kl_realtype( + kltype_t * /* kltype_t pointer */, + int /* type number */); + +kltype_t *kl_find_typenum( + uint64_t /* private typenumber */); + +int kl_get_first_similar_typedef( + char * /* type name */, + char * /* fullname */); + +int kl_type_size( + kltype_t * /* kltype_t pointer */); + +int kl_struct_len( + char * /* struct name */); + +kltype_t *kl_get_member( + kltype_t * /* kltype_t pointer */, + char * /* member name */); + +int kl_get_member_offset( + kltype_t * /* kltype_t pointer */, + char * /* member name */); + +int kl_is_member( + char * /* struct name */, + char * /* member name */); + +kltype_t *kl_member( + char * /* struct name */, + char * /* member name */); + +int kl_member_offset( + char * /* struct name */, + char * /* member name */); + +int kl_member_size( + char * /* struct name */, + char * /* member name */); + +/* cpw: get rid of last arguent FILE * */ +void kl_print_member(void *, kltype_t *, int, int); +void kl_print_pointer_type(void *, kltype_t *, int, int); +void kl_print_function_type(void *, kltype_t *, int, int); +void kl_print_array_type(void *, kltype_t *, int, int); +void kl_print_enumeration_type(void *, kltype_t *, int, int); +void kl_print_base_type(void *, kltype_t *, int, int); +void kl_print_type(void *, kltype_t *, int, int); +void kl_print_struct_type(void *, kltype_t *, int, int); +void kl_print_base_value(void *, kltype_t *, int); + +void kl_print_type( + void * /* pointer to data */, + kltype_t * /* pointer to type information */, + int /* indent level */, + int /* flags */); + +#endif /* __KL_TYPEINFO_H */ --- /dev/null +++ b/kdb/modules/lcrash/kl_types.h @@ -0,0 +1,54 @@ +/* + * $Id: kl_types.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +#ifndef __KL_TYPES_H +#define __KL_TYPES_H + +/* The following typedef should be used for variables or return values + * that contain kernel virtual or physical addresses. It should be sized + * such that it can hold both pointers of 64 bit architectures as well as + * pointers from 32 bit architectures. + */ +typedef unsigned long kaddr_t; + +/* The following typedef should be used when converting a pointer value + * (either kernel or application) to an unsigned value for pointer + * calculations. + */ +typedef unsigned long uaddr_t; + +/* KLIB error type + */ +typedef uint64_t k_error_t; + +/* Typedef that allows a single fprintf() call to work for both + * 32-bit and 64-bit pointer values. + */ +#define UADDR(X) ((kaddr_t)X) +#define UADDR64(X) ((kaddr_t)X)) +/* #define UADDR(X) ((uaddr_t)X) */ +/* #define UADDR64(X) ((uint64_t)((uaddr_t)X)) */ + + +/* cpw */ +/* was: #include */ +#include "asm/kl_types.h" + +#endif /* __KL_TYPES_H */ --- /dev/null +++ b/kdb/modules/lcrash/klib.h @@ -0,0 +1,480 @@ +/* + * $Id: klib.h 1336 2006-10-23 23:27:06Z tjm $ + * + * This file is part of libklib. + * A library which provides access to Linux system kernel dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, NEC, and others + * + * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright 2000 Junichi Nomura, NEC Solutions + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. See the file COPYING for more + * information. + */ + +/* + * klib.h -- Interface of the klib library, a library for access to + * Linux system memory dumps. + */ + +#ifndef __KLIB_H +#define __KLIB_H + +/* Include header files + */ +#if 0 + /* cpw: don't include all this: */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +/* cpw: change all the below includes form the < > form to " " */ + +/* Include libutil header + */ +#include "kl_lib.h" + +/* Include libklib header files + */ +#include "kl_types.h" +#include "kl_error.h" +#include "kl_dump.h" +#include "kl_mem.h" +#include "kl_cmp.h" +#include "kl_typeinfo.h" +#include "kl_module.h" +#include "kl_sym.h" +#include "kl_bfd.h" +#include "kl_debug.h" +#include "kl_stabs.h" +#include "kl_dwarfs.h" +#include "kl_task.h" +#include "kl_dump_arch.h" + + +#ifndef TRUE +# define TRUE 1 +#endif +#ifndef FALSE +# define FALSE 0 +#endif + +#ifndef MIN +#define MIN(x,y) (((x)<(y))?(x):(y)) +#endif +#ifndef MAX +#define MAX(x,y) (((x)>(y))?(x):(y)) +#endif + +#define KL_NR_CPUS 128 + +/* We have to distinc between HOST_ARCH_* and DUMP_ARCH_*. These two classes of + * macros are used througout the code for conditional compilation. + * Additional we have following macros for comparison and switch statements. + */ +#define KL_ARCH_UNKNOWN 0 +#define KL_ARCH_ALPHA 1 +#define KL_ARCH_ARM 2 +#define KL_ARCH_I386 3 +#define KL_ARCH_IA64 4 +#define KL_ARCH_M68K 5 +#define KL_ARCH_MIPS 6 +#define KL_ARCH_MIPS64 7 +#define KL_ARCH_PPC 8 +#define KL_ARCH_S390 9 +#define KL_ARCH_SH 10 +#define KL_ARCH_SPARK 11 +#define KL_ARCH_SPARK64 12 +#define KL_ARCH_S390X 13 +#define KL_ARCH_PPC64 14 +#define KL_ARCH_X86_64 15 +#define KL_ARCH_IA64_SN2 16 +#define KL_ARCH_IA64_DIG 17 +#define KL_ARCH_IA64_HPSIM 18 +#define KL_ARCH_IA64_HPZX1 19 +#define KL_ARCH_S390SA 20 + +#define KL_LIVE_SYSTEM 1000 + +#define ARCH_IS_IA64(A) \ + ((A==KL_ARCH_IA64)|| \ + (A==KL_ARCH_IA64_SN2)|| \ + (A==KL_ARCH_IA64_DIG)|| \ + (A==KL_ARCH_IA64_HPSIM)|| \ + (A==KL_ARCH_IA64_HPZX1)) + +#ifdef HOST_ARCH_ALPHA +# define KL_HOST_ARCH KL_ARCH_ALPHA +#endif +#ifdef HOST_ARCH_ARM +# define KL_HOST_ARCH KL_ARCH_ARM +#endif +#ifdef HOST_ARCH_I386 +# define KL_HOST_ARCH KL_ARCH_I386 +#endif +#ifdef HOST_ARCH_IA64 +# define KL_HOST_ARCH KL_ARCH_IA64 +#endif +#ifdef HOST_ARCH_S390 +# define KL_HOST_ARCH KL_ARCH_S390 +#endif +#ifdef HOST_ARCH_S390X +# define KL_HOST_ARCH KL_ARCH_S390X +#endif +#ifdef HOST_ARCH_PPC64 +#define KL_HOST_ARCH KL_ARCH_PPC64 +#endif +#ifdef HOST_ARCH_X86_64 +#define KL_HOST_ARCH KL_ARCH_X86_64 +#endif + +#define KL_ARCH_STR_ALPHA "alpha" +#define KL_ARCH_STR_ARM "arm" +#define KL_ARCH_STR_I386 "i386" +#define KL_ARCH_STR_IA64 "ia64" +#define KL_ARCH_STR_S390 "s390" +#define KL_ARCH_STR_S390X "s390x" +#define KL_ARCH_STR_PPC64 "ppc64" +#define KL_ARCH_STR_X86_64 "x86_64" +#define KL_ARCH_STR_IA64_SN2 "sn2" +#define KL_ARCH_STR_UNKNOWN "unknown" + +/* for endianess of dump and host arch + */ +#define KL_UNKNOWN_ENDIAN 0x00 +#define KL_LITTLE_ENDIAN 0x01 +#define KL_BIG_ENDIAN 0x02 + +/* macros for handling of different Kernel versions + */ +#define LINUX_2_2_X(R) (((R) & 0xffff00) == 0x020200) +#define LINUX_2_2_16 0x020210 +#define LINUX_2_2_17 0x020211 +#define LINUX_2_4_X(R) (((R) & 0xffff00) == 0x020400) +#define LINUX_2_4_0 0x020400 +#define LINUX_2_4_4 0x020404 +#define LINUX_2_4_15 0x02040f +#define LINUX_2_6_X(R) (((R) & 0xffff00) == 0x020600) +#define LINUX_2_6_0 0x020600 + +/* libklib flags + */ +#define KL_FAILSAFE_FLG 0x0001 +#define KL_NOVERIFY_FLG 0x0002 +#define KL_SILENT_FLG 0x0004 +#define KL_SAVETYPES_FLG 0x0008 +#define KL_USETYPES_FLG 0x0010 + +/* macros for backward compatibility + */ +#define NUM_PHYSPAGES KLP->dump->mem.num_physpages +#define MEM_MAP KLP->dump->mem.mem_map +#define KL_HIGH_MEMORY KLP->dump->mem.high_memory +#define KL_INIT_MM KLP->dump->mem.init_mm +#define KL_NUM_CPUS KLP->dump->mem.num_cpus +#define KL_PGDAT_LIST KLP->dump->mem.pgdat_list + +/* macros for better use of dump architecture dependent functions + */ + +/* read integer value from buffer */ +#define KL_GET_PTR(ptr) (*KLP->dump->func.get_ptr)(ptr) +#define KL_GET_LONG(ptr) ((int64_t) KL_GET_PTR(ptr)) +#define KL_GET_ULONG(ptr) KL_GET_PTR(ptr) +#define KL_GET_UINT8(ptr) (*KLP->dump->func.get_uint8)(ptr) +#define KL_GET_UINT16(ptr) (*KLP->dump->func.get_uint16)(ptr) +#define KL_GET_UINT32(ptr) (*KLP->dump->func.get_uint32)(ptr) +#define KL_GET_UINT64(ptr) (*KLP->dump->func.get_uint64)(ptr) +#define KL_GET_INT8(ptr) ((int8_t) KL_GET_UINT8(ptr)) +#define KL_GET_INT16(ptr) ((int16_t) KL_GET_UINT16(ptr)) +#define KL_GET_INT32(ptr) ((int32_t) KL_GET_UINT32(ptr)) +#define KL_GET_INT64(ptr) ((int64_t) KL_GET_UINT64(ptr)) + +/* read integer value from dump (without address mapping) + * Use these functions sparsely, e.g. before address translation + * is properly set up. + */ +#define KL_READ_PTR(addr) (*KLP->dump->func.read_ptr)(addr) +#define KL_READ_LONG(addr) ((int64_t) KL_READ_PTR(addr)) +#define KL_READ_ULONG(addr) KL_READ_PTR(addr) +#define KL_READ_UINT8(addr) (*KLP->dump->func.read_uint8)(addr) +#define KL_READ_UINT16(addr) (*KLP->dump->func.read_uint16)(addr) +#define KL_READ_UINT32(addr) (*KLP->dump->func.read_uint32)(addr) +#define KL_READ_UINT64(addr) (*KLP->dump->func.read_uint64)(addr) +#define KL_READ_INT8(addr) ((int8_t) KL_READ_UINT8(addr)) +#define KL_READ_INT16(addr) ((int16_t) KL_READ_UINT16(addr)) +#define KL_READ_INT32(addr) ((int32_t) KL_READ_UINT32(addr)) +#define KL_READ_INT64(addr) ((int64_t) KL_READ_UINT64(addr)) + +/* read integer value from dump (from virtual address) doing address mapping */ +#define KL_VREAD_PTR(addr) (*KLP->dump->func.vread_ptr)(addr) +#define KL_VREAD_LONG(addr) ((int64_t) KL_VREAD_PTR(addr)) +#define KL_VREAD_ULONG(addr) KL_VREAD_PTR(addr) +#define KL_VREAD_UINT8(addr) (*KLP->dump->func.vread_uint8)(addr) +#define KL_VREAD_UINT16(addr) (*KLP->dump->func.vread_uint16)(addr) +#define KL_VREAD_UINT32(addr) (*KLP->dump->func.vread_uint32)(addr) +#define KL_VREAD_UINT64(addr) (*KLP->dump->func.vread_uint64)(addr) +#define KL_VREAD_INT8(addr) ((int8_t) KL_VREAD_UINT8(addr)) +#define KL_VREAD_INT16(addr) ((int16_t) KL_VREAD_UINT16(addr)) +#define KL_VREAD_INT32(addr) ((int32_t) KL_VREAD_UINT32(addr)) +#define KL_VREAD_INT64(addr) ((int64_t) KL_VREAD_UINT64(addr)) + +/* determine start of stack */ +#define KL_KERNELSTACK_UINT64 (*KLP->dump->arch.kernelstack) +/* map virtual adress to physical one */ +#define KL_VIRTOP (*KLP->dump->arch.virtop) +/* travers page table */ +#define KL_MMAP_VIRTOP (*KLP->dump->arch.mmap_virtop) +/* check whether address points to valid physical memory */ +#define KL_VALID_PHYSMEM (*KLP->dump->arch.valid_physmem) +/* determine next valid physical address */ +#define KL_NEXT_VALID_PHYSADDR (*KLP->dump->arch.next_valid_physaddr) +/* XXX */ +#define KL_FIX_VADDR (*KLP->dump->arch.fix_vaddr) +/* write dump_header_asm_t */ +#define KL_WRITE_DHA (*KLP->dump->arch.write_dha) +/* size of dump_header_asm_t */ +#define KL_DHA_SIZE (KLP->dump->arch.dha_size) +/* init virtual to physical address mapping */ +#define KL_INIT_VIRTOP (KLP->dump->arch.init_virtop) + + +/* macros for easier access to dump specific values */ +#define KL_CORE_TYPE KLP->dump->core_type +#define KL_CORE_FD KLP->dump->core_fd +#define KL_ARCH KLP->dump->arch.arch +#define KL_PTRSZ KLP->dump->arch.ptrsz +#define KL_NBPW (KL_PTRSZ/8) +#define KL_BYTE_ORDER KLP->dump->arch.byteorder +#define KL_PAGE_SHIFT KLP->dump->arch.pageshift +#define KL_PAGE_SIZE KLP->dump->arch.pagesize +#define KL_PAGE_MASK KLP->dump->arch.pagemask +#define KL_PAGE_OFFSET KLP->dump->arch.pageoffset +#define KL_STACK_OFFSET KLP->dump->arch.kstacksize +#define IS_BIG_ENDIAN() (KL_BYTE_ORDER == KL_BIG_ENDIAN) +#define IS_LITTLE_ENDIAN() (KL_BYTE_ORDER == KL_LITTLE_ENDIAN) +#define KL_LINUX_RELEASE KLP->dump->mem.linux_release +#define KL_KERNEL_FLAGS KLP->dump->mem.kernel_flags + +#if 0 +/* cpw: don't need all this dump file stuff: */ +/* macros to access input files */ +#define KL_MAP_FILE KLP->dump->map +#define KL_DUMP_FILE KLP->dump->dump +#define KL_KERNTYPES_FILE KLP->kerntypes + +#define CORE_IS_KMEM (KL_CORE_TYPE == dev_kmem) +#define CORE_IS_DUMP ((KL_CORE_TYPE > dev_kmem) && (KL_CORE_TYPE <= unk_core)) + + +/* Generic dump header structure (the first three members of + * dump_header and dump_header_asm are the same). + */ +typedef struct generic_dump_header_s { + uint64_t magic_number; + uint32_t version; + uint32_t header_size; +} generic_dump_header_t; + +/* Some macros for making it easier to access the generic header + * information in a dump_header or dump_header_asm stuct. + */ +#define DHP(dh) ((generic_dump_header_t*)(dh)) +#define DH_MAGIC(dh) DHP(dh)->magic_number +#define DH_VERSION(dh) DHP(dh)->version +#define DH_HEADER_SIZE(dh) DHP(dh)->header_size + +extern kl_dump_header_t *DUMP_HEADER; +extern void *DUMP_HEADER_ASM; +#endif + +/* Struct to store some host architecture specific values + */ +typedef struct kl_hostarch_s { + int arch; /* KL_ARCH_ */ + int ptrsz; /* 32 or 64 bit */ + int byteorder; /* KL_LITTLE_ENDIAN or KL_BIG_ENDIAN */ +} kl_hostarch_t; + +/* Struct klib_s, contains all the information necessary for accessing + * information in the kernel. A pointer to a klib_t struct will be + * returned from libkern_init() if core dump analysis (or live system + * analysis) is possible. + * + */ +typedef struct klib_s { + int k_flags; /* Flags pertaining to klib_s struct */ + kl_hostarch_t *host; /* host arch info */ + kl_dumpinfo_t *dump; /* dump information */ + maplist_t *k_symmap; /* symbol information */ + kltype_t *k_typeinfo; /* type information */ + char *kerntypes; /* pathname for kerntypes file */ +} klib_t; + +/* Structure to accomodate all debug formats */ +struct namelist_format_opns { + /* to open/setup the namelist file */ + int (*open_namelist) (char *filename , int flags); + int (*setup_typeinfo)(void); +}; + +/* + * global variables + */ + +/* Here we store almost everything, we need to know about a dump. */ +extern klib_t *KLP; + +/* macros to make live easier */ +#define MIP KLP->dump +#define STP KLP->k_symmap +#define TASK_STRUCT_SZ (KLP->dump->mem.struct_sizes.task_struct_sz) +#define MM_STRUCT_SZ (KLP->dump->mem.struct_sizes.mm_struct_sz) +#define PAGE_SZ (KLP->dump->mem.struct_sizes.page_sz) +#define MODULE_SZ (KLP->dump->mem.struct_sizes.module_sz) +#define NEW_UTSNAME_SZ (KLP->dump->mem.struct_sizes.new_utsname_sz) +#define SWITCH_STACK_SZ (KLP->dump->mem.struct_sizes.switch_stack_sz) +#define PT_REGS_SZ (KLP->dump->mem.struct_sizes.pt_regs_sz) +#define PGLIST_DATA_SZ (KLP->dump->mem.struct_sizes.pglist_data_sz) +#define RUNQUEUE_SZ (KLP->dump->mem.struct_sizes.runqueue_sz) + +#if 0 +cpw: used for sial? +/* klib_jbuf has to be defined outside libklib. + * Make sure to call setjmp(klib_jbuf) BEFORE kl_sig_setup() is called! */ +extern jmp_buf klib_jbuf; +#endif + +/* Macros that eliminate the offset paramaters to the kl_uint() and kl_int() + * functions (just makes things cleaner looking) + */ +#define KL_UINT(p, s, m) kl_uint(p, s, m, 0) +#define KL_INT(p, s, m) kl_int(p, s, m, 0) + +/* Macros for translating strings into long numeric values depending + * on the base of 's'. + */ +#define GET_VALUE(s, value) kl_get_value(s, NULL, 0, value) +#define GET_HEX_VALUE(s) (kaddr_t)strtoull(s, (char**)NULL, 16) +#define GET_DEC_VALUE(s) (unsigned)strtoull(s, (char**)NULL, 10) +#define GET_OCT_VALUE(s) (unsigned)strtoull(s, (char**)NULL, 8) + +#define KL_SIGFLG_CORE 0x1 +#define KL_SIGFLG_SILENT 0x2 +#define KL_SIGFLG_LNGJMP 0x4 + +/* Flag that tells kl_is_valid_kaddr() to perform a word aligned check + */ +#define WORD_ALIGN_FLAG 1 + +#define ADDR_TO_PGNO(addr) ((addr - KL_PAGE_OFFSET) >> KL_PAGE_SHIFT); + +/* Generalized macros for pointing at different data types at particular + * offsets in kernel structs. + */ +/* #define K_ADDR(p, s, f) ((uaddr_t)(p) + kl_member_offset(s, f)) */ +#define K_ADDR(p, s, f) ((p) + kl_member_offset(s, f)) +#define K_PTR(p, s, f) (K_ADDR((void*)p, s, f)) +#define CHAR(p, s, f) (K_ADDR((char*)p, s, f)) + +#define PTRSZ32 ((KL_PTRSZ == 32) ? 1 : 0) +#define PTRSZ64 ((KL_PTRSZ == 64) ? 1 : 0) + +/* Function prototypes + */ +/* cpw: remove the last argument FILE * */ +void kl_binary_print(uint64_t); +void kl_print_bit_value(void *, int, int, int, int); +void kl_print_char(void *, int); +void kl_print_uchar(void *, int); +void kl_print_int2(void *, int); +void kl_print_uint2(void *, int); +void kl_print_int4(void *, int); +void kl_print_uint4(void *, int); +void kl_print_float4(void *, int); +void kl_print_int8(void *, int); +void kl_print_uint8(void *, int); +void kl_print_float8(void *, int); +void kl_print_base(void *, int, int, int); +void kl_print_string(char *); + +int kl_get_live_filenames( + char * /* pointer to buffer for map filename */, + char * /* pointer to buffer for dump filename */, + char * /* pointer to buffer for namelist filename */); + +int kl_init_klib( + char * /* map file name */, + char * /* dump file name */, + char * /* namelist file name */, + int /* system arch of memory in dump */, + int /* rwflag flag (/dev/mem only) */, + int /* Linux release */); + +void kl_free_klib( + klib_t * /* Pointer to klib_s struct */); + + +int kl_dump_retrieve( + char * /* dumpdev name */, + char * /* dumpdir name */, + int /* progress flag (zero or non-zero) */, + int /* debug flag (zero or non-zero) */); + +int kl_dump_erase( + char * /* dumpdev name */); + +uint64_t kl_strtoull( + char * /* string containing numeric value */, + char ** /* pointer to pointer to bad char */, + int /* base */); + +int kl_get_value( + char * /* param */, + int * /* mode pointer */, + int /* number of elements */, + uint64_t * /* pointer to value */); + +/* Functions for working with list_head structs + */ +kaddr_t kl_list_entry(kaddr_t, char *, char *); +kaddr_t kl_list_next(kaddr_t); +kaddr_t kl_list_prev(kaddr_t); + +int kl_sig_setup(int); + +void kl_set_curnmlist( + int /* index of namelist */); + +int kl_open_namelist( + char * /* name of namelist */, + int /* flags */, + int /* kl_flags */); + +int kl_get_structure(kaddr_t, char*, size_t*, void**); +uint64_t kl_get_bit_value(void*, unsigned int, unsigned int, unsigned int); +void kl_s390tod_to_timeval(uint64_t, struct timeval*); + +#endif /* __KLIB_H */ --- /dev/null +++ b/kdb/modules/lcrash/lc_eval.h @@ -0,0 +1,225 @@ +/* + * $Id: lc_eval.h 1122 2004-12-21 23:26:23Z tjm $ + * + * This file is part of lcrash, an analysis tool for Linux memory dumps. + * + * Created by Silicon Graphics, Inc. + * Contributions by IBM, and others + * + * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation + * + * 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. See the file COPYING for more + * information. + */ + +#ifndef __LC_EVAL_H +#define __LC_EVAL_H + +typedef struct type_s { + int flag; + union { + struct type_s *next; + kltype_t *kltp; + } un; +} type_t; + +#define t_next un.next +#define t_kltp un.kltp + +/* Structure to hold info on "tokens" extracted from eval and print + * command input strings. + */ +typedef struct token_s { + short type; + short operator; /* if token is an operator */ + char *string; /* string holding value or identifier */ + char *ptr; /* pointer to start of token */ + struct token_s *next; /* next token in the chain */ +} token_t; + +/* Structure returned by the eval() function containing the result + * of an expression evaluation. This struct is also used to build the + * parse tree for the expression. + */ +typedef struct node_s { + struct node_s *next; /* linked list pointer */ + unsigned char node_type; /* type of node */ + unsigned short flags; /* see below */ + unsigned char operator; /* operator if node is type OPERATOR */ + unsigned char byte_size; /* byte_size of base_type values */ + char *name; /* name of variable or struct member */ + /* value and address are uint64_t in lcrash, but for ia32 ... */ + unsigned long long value; /* numeric value or pointer */ + unsigned long address; /* address (could be same as pointer) */ + type_t *type; /* pointer to type related info */ + char *tok_ptr; /* pointer to token in cmd string */ + struct node_s *left; /* pointer to left child */ + struct node_s *right; /* pointer to right child */ +} node_t; + +/* Token and Node types + */ +#define OPERATOR 1 +#define NUMBER 2 +#define INDEX 3 +#define TYPE_DEF 4 +#define VADDR 5 +#define MEMBER 6 +#define STRING 7 +#define TEXT 8 +#define CHARACTER 9 +#define EVAL_VAR 10 + +/* Flag values + */ +#define STRING_FLAG 0x001 +#define ADDRESS_FLAG 0x002 +#define INDIRECTION_FLAG 0x004 +#define POINTER_FLAG 0x008 +#define MEMBER_FLAG 0x010 +#define BOOLIAN_FLAG 0x020 +#define KLTYPE_FLAG 0x040 +#define NOTYPE_FLAG 0x080 +#define UNSIGNED_FLAG 0x100 +#define VOID_FLAG 0x200 + +/* Flag value for print_eval_error() function + */ +#define CMD_NAME_FLG 1 /* cmdname is not name of a command */ +#define CMD_STRING_FLG 2 /* cmdname is not name of a command */ + +/* Expression operators in order of precedence. + */ +#define CONDITIONAL 1 +#define CONDITIONAL_ELSE 2 +#define LOGICAL_OR 3 +#define LOGICAL_AND 4 +#define BITWISE_OR 5 +#define BITWISE_EXCLUSIVE_OR 6 +#define BITWISE_AND 7 +#define EQUAL 8 +#define NOT_EQUAL 9 +#define LESS_THAN 10 +#define GREATER_THAN 11 +#define LESS_THAN_OR_EQUAL 12 +#define GREATER_THAN_OR_EQUAL 13 +#define RIGHT_SHIFT 14 +#define LEFT_SHIFT 15 +#define ADD 16 +#define SUBTRACT 17 +#define MULTIPLY 18 +#define DIVIDE 19 +#define MODULUS 20 +#define LOGICAL_NEGATION 21 +#define ONES_COMPLEMENT 22 +#define PREFIX_INCREMENT 23 +#define PREFIX_DECREMENT 24 +#define POSTFIX_INCREMENT 25 +#define POSTFIX_DECREMENT 26 +#define CAST 27 +#define UNARY_MINUS 28 +#define UNARY_PLUS 29 +#define INDIRECTION 30 +#define ADDRESS 31 +#define SIZEOF 32 +#define RIGHT_ARROW 33 +#define DOT 34 +#define OPEN_PAREN 100 +#define CLOSE_PAREN 101 +#define OPEN_SQUARE_BRACKET 102 +#define CLOSE_SQUARE_BRACKET 103 +#define SEMI_COLON 104 +#define NOT_YET -1 + +/* Errors codes primarily for use with eval (print) functions + */ +#define E_OPEN_PAREN 1100 +#define E_CLOSE_PAREN 1101 +#define E_BAD_STRUCTURE 1102 +#define E_MISSING_STRUCTURE 1103 +#define E_BAD_MEMBER 1104 +#define E_BAD_OPERATOR 1105 +#define E_BAD_OPERAND 1106 +#define E_MISSING_OPERAND 1107 +#define E_BAD_TYPE 1108 +#define E_NOTYPE 1109 +#define E_BAD_POINTER 1110 +#define E_BAD_INDEX 1111 +#define E_BAD_CHAR 1112 +#define E_BAD_STRING 1113 +#define E_END_EXPECTED 1114 +#define E_BAD_EVAR 1115 /* Bad eval variable */ +#define E_BAD_VALUE 1116 +#define E_NO_VALUE 1117 +#define E_DIVIDE_BY_ZERO 1118 +#define E_BAD_CAST 1119 +#define E_NO_ADDRESS 1120 +#define E_SINGLE_QUOTE 1121 + +#define E_BAD_WHATIS 1197 +#define E_NOT_IMPLEMENTED 1198 +#define E_SYNTAX_ERROR 1199 + +extern uint64_t eval_error; +extern char *error_token; + +/* Function prototypes + */ +node_t *eval(char **, int); +void print_eval_error(char *, char *, char *, uint64_t, int); +void free_nodes(node_t *); + +/* Struct to hold information about eval variables + */ +typedef struct variable_s { + btnode_t v_bt; /* Must be first */ + int v_flags; + char *v_exp; /* What was entered on command line */ + char *v_typestr; /* Actual type string after eval() call */ + node_t *v_node; +} variable_t; + +#define v_left v_bt.bt_left +#define v_right v_bt.bt_right +#define v_name v_bt.bt_key + +/* Flag values + */ +#define V_PERM 0x001 /* can't be unset - can be modified */ +#define V_DEFAULT 0x002 /* set at startup */ +#define V_NOMOD 0x004 /* cannot be modified */ +#define V_TYPEDEF 0x008 /* contains typed data */ +#define V_REC_STRUCT 0x010 /* direct ref to struct/member (not pointer) */ +#define V_STRING 0x020 /* contains ASCII string (no type) */ +#define V_COMMAND 0x040 /* contains command string (no type) */ +#define V_OPTION 0x080 /* contains option flag (e.g., $hexints) */ +#define V_PERM_NODE 0x100 /* Don't free node after setting variable */ + +/* Variable table struct + */ +typedef struct vtab_s { + variable_t *vt_root; + int vt_count; +} vtab_t; + +extern vtab_t *vtab; /* Pointer to table of eval variable info */ + +/* Function Prototypes + */ +variable_t *make_variable(char *, char *, node_t *, int); +void clean_variable(variable_t *); +void free_variable(variable_t *); +void init_variables(vtab_t *); +int set_variable(vtab_t *, char *, char *, node_t *, int); +int unset_variable(vtab_t *, variable_t *); +variable_t *find_variable(vtab_t *, char *, int); +kltype_t *number_to_type(node_t *); +void free_eval_memory(void); +/* cpw: was int print_eval_results(node_t *, FILE *, int); */ +int print_eval_results(node_t *, int); + +#endif /* __LC_EVAL_H */ --- a/kernel/exit.c +++ b/kernel/exit.c @@ -4,6 +4,9 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ +#ifdef CONFIG_KDB +#include +#endif #include #include #include --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -529,3 +529,26 @@ static int __init kallsyms_init(void) return 0; } device_initcall(kallsyms_init); + + +#ifdef CONFIG_KDB +#include +#include + +const char *kdb_walk_kallsyms(loff_t *pos) +{ + static struct kallsym_iter kdb_walk_kallsyms_iter; + if (*pos == 0) { + memset(&kdb_walk_kallsyms_iter, 0, sizeof(kdb_walk_kallsyms_iter)); + reset_iter(&kdb_walk_kallsyms_iter, 0); + } + while (1) { + if (!update_iter(&kdb_walk_kallsyms_iter, *pos)) + return NULL; + ++*pos; + /* Some debugging symbols have no name. Ignore them. */ + if (kdb_walk_kallsyms_iter.name[0]) + return kdb_walk_kallsyms_iter.name; + } +} +#endif /* CONFIG_KDB */ --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -40,6 +40,12 @@ #include #include +#ifdef CONFIG_KDB_KDUMP +#include +#include +#include +#endif + /* Per cpu memory for storing cpu states in case of system crash. */ note_buf_t __percpu *crash_notes; @@ -1080,7 +1086,16 @@ void crash_kexec(struct pt_regs *regs) crash_setup_regs(&fixed_regs, regs); crash_save_vmcoreinfo(); + /* + * If we enabled KDB, we don't want to automatically + * perform a kdump since KDB will be responsible for + * executing kdb through a special 'kdump' command. + */ +#ifdef CONFIG_KDB_KDUMP + kdba_kdump_prepare(&fixed_regs); +#else machine_crash_shutdown(&fixed_regs); +#endif machine_kexec(kexec_crash_image); } mutex_unlock(&kexec_mutex); --- a/kernel/module.c +++ b/kernel/module.c @@ -2786,12 +2786,23 @@ out: return -ERANGE; } +#ifdef CONFIG_KDB +#include +struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */ +#endif /* CONFIG_KDB */ + int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, char *name, char *module_name, int *exported) { struct module *mod; +#ifdef CONFIG_KDB + int get_lock = !KDB_IS_RUNNING(); +#else +#define get_lock 1 +#endif - preempt_disable(); + if (get_lock) + preempt_disable(); list_for_each_entry_rcu(mod, &modules, list) { if (symnum < mod->num_symtab) { *value = mod->symtab[symnum].st_value; @@ -2800,12 +2811,14 @@ int module_get_kallsym(unsigned int symn KSYM_NAME_LEN); strlcpy(module_name, mod->name, MODULE_NAME_LEN); *exported = is_exported(name, *value, mod); - preempt_enable(); + if (get_lock) + preempt_enable(); return 0; } symnum -= mod->num_symtab; } - preempt_enable(); + if (get_lock) + preempt_enable(); return -ERANGE; } --- a/kernel/panic.c +++ b/kernel/panic.c @@ -23,6 +23,9 @@ #include #include #include +#ifdef CONFIG_KDB_KDUMP +#include +#endif int panic_on_oops; static unsigned long tainted_mask; @@ -96,6 +99,12 @@ NORET_TYPE void panic(const char * fmt, dump_stack(); #endif + +#ifdef CONFIG_KDB_KDUMP + if (kdb_kdump_state == KDB_KDUMP_RESET) { + (void)kdb(KDB_REASON_OOPS, 999, get_irq_regs()); + } +#endif /* * If we have crashed and we have a crash kernel loaded let it handle * everything else. --- a/kernel/sched.c +++ b/kernel/sched.c @@ -7948,7 +7948,7 @@ void normalize_rt_tasks(void) #endif /* CONFIG_MAGIC_SYSRQ */ -#ifdef CONFIG_IA64 +#if defined(CONFIG_IA64) || defined(CONFIG_KDB) /* * These functions are only useful for the IA64 MCA handling. * @@ -9196,3 +9196,110 @@ void synchronize_sched_expedited(void) EXPORT_SYMBOL_GPL(synchronize_sched_expedited); #endif /* #else #ifndef CONFIG_SMP */ + + +#ifdef CONFIG_KDB +#include + +static void +kdb_prio(char *name, struct rt_prio_array *array, kdb_printf_t xxx_printf, + unsigned int cpu) +{ + int pri, printed_header = 0; + struct task_struct *p; + + xxx_printf(" %s rt bitmap: 0x%lx 0x%lx 0x%lx\n", + name, + array->bitmap[0], array->bitmap[1], array->bitmap[2]); + + pri = sched_find_first_bit(array->bitmap); + if (pri < MAX_RT_PRIO) { + xxx_printf(" rt bitmap priorities:"); + while (pri < MAX_RT_PRIO) { + xxx_printf(" %d", pri); + pri++; + pri = find_next_bit(array->bitmap, MAX_RT_PRIO, pri); + } + xxx_printf("\n"); + } + + for (pri = 0; pri < MAX_RT_PRIO; pri++) { + int printed_hdr = 0; + struct list_head *head, *curr; + + head = array->queue + pri; + curr = head->next; + while(curr != head) { + struct task_struct *task; + if (!printed_hdr) { + xxx_printf(" queue at priority=%d\n", pri); + printed_hdr = 1; + } + task = list_entry(curr, struct task_struct, rt.run_list); + if (task) + xxx_printf(" 0x%p %d %s time_slice:%d\n", + task, task->pid, task->comm, + task->rt.time_slice); + curr = curr->next; + } + } + for_each_process(p) { + if (p->se.on_rq && (task_cpu(p) == cpu) && + (p->policy == SCHED_NORMAL)) { + if (!printed_header) { + xxx_printf(" sched_normal queue:\n"); + printed_header = 1; + } + xxx_printf(" 0x%p %d %s pri:%d spri:%d npri:%d\n", + p, p->pid, p->comm, p->prio, + p->static_prio, p->normal_prio); + } + } +} + +/* This code must be in sched.c because struct rq is only defined in this + * source. To allow most of kdb to be modular, this code cannot call any kdb + * functions directly, any external functions that it needs must be passed in + * as parameters. + */ + +void +kdb_runqueue(unsigned long cpu, kdb_printf_t xxx_printf) +{ + int i; + struct rq *rq; + + rq = cpu_rq(cpu); + + xxx_printf("CPU%ld lock:%s curr:0x%p(%d)(%s)", + cpu, (raw_spin_is_locked(&rq->lock))?"LOCKED":"free", + rq->curr, rq->curr->pid, rq->curr->comm); + if (rq->curr == rq->idle) + xxx_printf(" is idle"); + xxx_printf("\n"); + + xxx_printf(" nr_running:%ld ", rq->nr_running); + xxx_printf(" nr_uninterruptible:%ld ", rq->nr_uninterruptible); + + xxx_printf(" nr_switches:%llu ", (long long)rq->nr_switches); + xxx_printf(" nr_iowait:%u ", atomic_read(&rq->nr_iowait)); + xxx_printf(" next_balance:%lu\n", rq->next_balance); + +#ifdef CONFIG_SMP + xxx_printf(" active_balance:%u ", rq->active_balance); + xxx_printf(" idle_at_tick:%u\n", rq->idle_at_tick); + + xxx_printf(" push_cpu:%u ", rq->push_cpu); + xxx_printf(" cpu:%u ", rq->cpu); + xxx_printf(" online:%u\n", rq->online); +#endif + + xxx_printf(" cpu_load:"); + for (i=0; icpu_load[i]); + xxx_printf("\n"); + kdb_prio("active", &rq->rt.active, xxx_printf, (unsigned int)cpu); +} +EXPORT_SYMBOL(kdb_runqueue); + +#endif /* CONFIG_KDB */ --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2735,3 +2735,52 @@ void __init signals_init(void) { sigqueue_cachep = KMEM_CACHE(sigqueue, SLAB_PANIC); } + +#ifdef CONFIG_KDB +#include +/* + * kdb_send_sig_info + * + * Allows kdb to send signals without exposing signal internals. + * + * Inputs: + * t task + * siginfo signal information + * seqno current kdb sequence number (avoid including kdbprivate.h) + * Outputs: + * None. + * Returns: + * None. + * Locking: + * Checks if the required locks are available before calling the main + * signal code, to avoid kdb deadlocks. + * Remarks: + */ +void +kdb_send_sig_info(struct task_struct *t, struct siginfo *info, int seqno) +{ + static struct task_struct *kdb_prev_t; + static int kdb_prev_seqno; + int sig, new_t; + if (!spin_trylock(&t->sighand->siglock)) { + kdb_printf("Can't do kill command now.\n" + "The sigmask lock is held somewhere else in kernel, try again later\n"); + return; + } + spin_unlock(&t->sighand->siglock); + new_t = kdb_prev_t != t || kdb_prev_seqno != seqno; + kdb_prev_t = t; + kdb_prev_seqno = seqno; + if (t->state != TASK_RUNNING && new_t) { + kdb_printf("Process is not RUNNING, sending a signal from kdb risks deadlock\n" + "on the run queue locks. The signal has _not_ been sent.\n" + "Reissue the kill command if you want to risk the deadlock.\n"); + return; + } + sig = info->si_signo; + if (send_sig_info(sig, info, t)) + kdb_printf("Fail to deliver Signal %d to process %d.\n", sig, t->pid); + else + kdb_printf("Signal %d is sent to process %d.\n", sig, t->pid); +} +#endif /* CONFIG_KDB */ --- a/lib/bug.c +++ b/lib/bug.c @@ -43,6 +43,10 @@ #include #include +#ifdef CONFIG_KDB +#include +#endif + extern const struct bug_entry __start___bug_table[], __stop___bug_table[]; static inline unsigned long bug_addr(const struct bug_entry *bug) @@ -177,5 +181,9 @@ enum bug_trap_type report_bug(unsigned l "[verbose debug info unavailable]\n", (void *)bugaddr); +#ifdef CONFIG_KDB + kdb(KDB_REASON_ENTER, 0, regs); +#endif + return BUG_TRAP_TYPE_BUG; } --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1929,6 +1929,28 @@ int hugetlb_overcommit_handler(struct ct #endif /* CONFIG_SYSCTL */ +#ifdef CONFIG_KDB +#include +#include +/* Like hugetlb_report_meminfo() but using kdb_printf() */ +void +kdb_hugetlb_report_meminfo(void) +{ + struct hstate *h = &default_hstate; + kdb_printf( + "HugePages_Total: %5lu\n" + "HugePages_Free: %5lu\n" + "HugePages_Rsvd: %5lu\n" + "HugePages_Surp: %5lu\n" + "Hugepagesize: %5lu kB\n", + h->nr_huge_pages, + h->free_huge_pages, + h->resv_huge_pages, + h->surplus_huge_pages, + 1UL << (huge_page_order(h) + PAGE_SHIFT - 10)); +} +#endif /* CONFIG_KDB */ + void hugetlb_report_meminfo(struct seq_file *m) { struct hstate *h = &default_hstate; --- a/mm/mmzone.c +++ b/mm/mmzone.c @@ -23,6 +23,10 @@ struct pglist_data *next_online_pgdat(st return NULL; return NODE_DATA(nid); } +#ifdef CONFIG_KDB +EXPORT_SYMBOL(first_online_pgdat); +EXPORT_SYMBOL(next_online_pgdat); +#endif /* * next_zone - helper magic for for_each_zone() --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -13,6 +13,10 @@ #include #include #include +#ifdef CONFIG_KDB +#include +#include +#endif /* CONFIG_KDB */ #include #include #include @@ -2129,6 +2133,24 @@ void si_swapinfo(struct sysinfo *val) spin_unlock(&swap_lock); } +#ifdef CONFIG_KDB +/* Like si_swapinfo() but without the locks */ +void kdb_si_swapinfo(struct sysinfo *val) +{ + unsigned int i; + unsigned long nr_to_be_unused = 0; + + for (i = 0; i < nr_swapfiles; i++) { + if (!(swap_info[i]->flags & SWP_USED) || + (swap_info[i]->flags & SWP_WRITEOK)) + continue; + nr_to_be_unused += swap_info[i]->inuse_pages; + } + val->freeswap = nr_swap_pages + nr_to_be_unused; + val->totalswap = total_swap_pages + nr_to_be_unused; +} +#endif /* CONFIG_KDB */ + /* * Verify that a swap entry is valid and increment its swap map count. *