32573 lines
908 KiB
Plaintext
32573 lines
908 KiB
Plaintext
From: Martin Hicks <mort@sgi.com>
|
||
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 <jeffm@suse.com>
|
||
---
|
||
|
||
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 <stack_address>'.
|
||
+Unlike other bt commands, 'bt <stack_address>' does not identify any specific
|
||
+process, instead it identifies a kernel stack. <stack_address> 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 <kmem_cache_alloc>'
|
||
+ 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 <getname+0x95>'
|
||
+ 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 <getname+0x51>'
|
||
+ 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 <getname+0x78>'
|
||
+ 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 <getname+0x56>'
|
||
+ 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 <strncpy_from_user>'
|
||
+ 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 <getname+0x72>'
|
||
+ 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 <getname+0x78>'
|
||
+ 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 <getname+0x89>'
|
||
+ 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 <getname+0x8e>'
|
||
+ 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 <getname+0x89>'
|
||
+ 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 <getname+0x95>'
|
||
+ 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 <getname+0x78>'
|
||
+ 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 <kmem_cache_free>'
|
||
+ 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 <getname+0x95>'
|
||
+ 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 \fB<esc>KDB\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 <esc>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 <variable_name> [<length>] [<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 <number>
|
||
+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 <cpu>
|
||
+Display the runqueues for the specified cpu.
|
||
+.TP 8
|
||
+rqa
|
||
+Display the runqueues for all cpus.
|
||
+.TP 8
|
||
+stackdepth <percentage>
|
||
+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 <kaos@sgi.com> - 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 [ <stack-frame-address> ]
|
||
+.LP
|
||
+btp <pid>
|
||
+.LP
|
||
+btt <struct-task-address>
|
||
+.LP
|
||
+bta [ DRSTZUIMA ]
|
||
+.LP
|
||
+btc [<cpu>]
|
||
+.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 <section_name> \\
|
||
+ --start-address=<start-address> \\
|
||
+ --stop-address=<end-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 <section_name> \\
|
||
+ --adjust-vma=<section-start> \\
|
||
+ --start-address=<start-address> \\
|
||
+ --stop-address=<end-address> \\
|
||
+ /path/to/module/<module-name>.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
|
||
+
|
||
+ <function_name> has memory parameters but no register parameters.
|
||
+ Assuming it is a 'pass through' function that does not refer to its register
|
||
+ parameters and setting <n> 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 <addr> <link-offset> <cmd>
|
||
+.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 <cmd> 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 <address>
|
||
+.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 <cpunum> Switch to new cpu
|
||
+
|
||
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
+%page
|
||
+
|
||
+KDB Commands
|
||
+
|
||
+
|
||
+ Memory Manipulation
|
||
+ md <vaddr> Display Memory Contents
|
||
+ mdr <vaddr> <bytes> Display Raw Memory
|
||
+ mds <vaddr> Display Symbolically
|
||
+ mm <vaddr> <value> Modify Memory Contents
|
||
+ id <vaddr> Display Instructions
|
||
+
|
||
+ Register Manipulation
|
||
+ rd Display Registers
|
||
+ rm <reg> <value> Modify Registers
|
||
+ ef <vaddr> Display exception frame
|
||
+
|
||
+ Stack
|
||
+ bt [<vaddr>] Stack traceback
|
||
+ btp <pid> Display stack for <pid>
|
||
+ bta Display all stacks
|
||
+
|
||
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
+%page
|
||
+
|
||
+KDB Commands
|
||
+
|
||
+
|
||
+ Breakpoint
|
||
+ bc <bpnum> Clear Breakpoint
|
||
+ bd <bpnum> Disable Breakpoint
|
||
+ be <bpnum> Enable Breakpoint
|
||
+ bl [<vaddr>] Display breakpoints
|
||
+ bp [<vaddr>] Set/Display breakpoints
|
||
+ bpa [<vaddr>] Set/Display global breakpoints
|
||
+ bph [<vaddr>] Set hardware breakpoint
|
||
+ bpha [<vaddr>] 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 [<vaddr>] Continue Execution
|
||
+ ss [<#steps>] Single Step
|
||
+ ssb Single step to branch/call
|
||
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
+%page
|
||
+
|
||
+KDB Commands
|
||
+
|
||
+
|
||
+ Kernel structures
|
||
+ ll <vaddr> <offset> <command> Traverse list and execute command
|
||
+ ps Display active task list
|
||
+ vm <vaddr> Display vm_area_struct
|
||
+ dentry <dentry> Display interesting dentry stuff
|
||
+ filp <filp> Display interesting filp stuff
|
||
+ sh <vaddr> Show scsi_host
|
||
+ sd <vaddr> Show scsi_device
|
||
+ sc <vaddr> Show scsi_cmnd
|
||
+ kiobuf <vaddr> Display kiobuf
|
||
+ page <vaddr> Display page
|
||
+ inode <vaddr> Display inode
|
||
+ bh <vaddr> Display buffer head
|
||
+ inode_pages <inode *> 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);
|
||
+
|
||
+ <snip>
|
||
+
|
||
+ 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"
|
||
+ }
|
||
+
|
||
+ <snip>
|
||
+
|
||
+ }
|
||
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
+%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"
|
||
+
|
||
+ <snip>
|
||
+
|
||
+ }
|
||
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
+%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
|
||
+ <snip>
|
||
+ 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
|
||
+ <snip>
|
||
+
|
||
+ 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
|
||
+
|
||
+ <snip>
|
||
+
|
||
+ 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;
|
||
+ <snip>
|
||
+ };
|
||
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
+%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;
|
||
+ <snip>
|
||
+ };
|
||
+
|
||
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
+%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"
|
||
+ <snip>
|
||
+ };
|
||
+ 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
|
||
+ <snip>
|
||
+ 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"
|
||
+ <snip>
|
||
+ };
|
||
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
+%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 <linux/reboot.h>
|
||
#include <linux/notifier.h>
|
||
#include <linux/jiffies.h>
|
||
+#ifdef CONFIG_KDB
|
||
+#include <linux/kdb.h>
|
||
+#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 <linux/kdb.h>
|
||
+#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 <linux/init.h>
|
||
#include <linux/usb/input.h>
|
||
#include <linux/hid.h>
|
||
+#ifdef CONFIG_KDB_USB
|
||
+#include <linux/kdb.h>
|
||
+#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 <asm/irq.h>
|
||
|
||
#include "8250.h"
|
||
-
|
||
#ifdef CONFIG_SPARC
|
||
#include "suncore.h"
|
||
#endif
|
||
|
||
+#ifdef CONFIG_KDB
|
||
+#include <linux/kdb.h>
|
||
+/*
|
||
+ * 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 <asm/fixmap.h>
|
||
#endif
|
||
|
||
+#ifdef CONFIG_KDB
|
||
+#include <linux/kdb.h>
|
||
+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 <linux/delay.h> /* for mdelay */
|
||
#include <linux/miscdevice.h>
|
||
#include <linux/serial_core.h>
|
||
+#ifdef CONFIG_KDB
|
||
+#include <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+#include <linux/serial_reg.h>
|
||
+/*
|
||
+ * 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 <asm/io.h>
|
||
#include <asm/sn/simulator.h>
|
||
@@ -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 <linux/workqueue.h>
|
||
#include <linux/mutex.h>
|
||
#include <linux/pm_runtime.h>
|
||
+#ifdef CONFIG_KDB_USB
|
||
+#include <linux/kdb.h>
|
||
+#endif
|
||
|
||
#include <linux/usb.h>
|
||
|
||
@@ -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 <linux/rwsem.h>
|
||
+#ifdef CONFIG_KDB_USB
|
||
+#include <linux/kdb.h>
|
||
+#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 <linux/kdb.h>
|
||
+#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 <linux/pci.h>
|
||
#include <linux/io.h>
|
||
|
||
+#ifdef CONFIG_KDB_USB
|
||
+#include <linux/kdb.h>
|
||
+#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 <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+#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 <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+/* 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 <asm/pgtable.h>
|
||
#include "internal.h"
|
||
|
||
+#ifdef CONFIG_KDB
|
||
+#include <linux/kdb.h>
|
||
+#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 <linux/kdb.h>
|
||
+#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 <kaos@sgi.com> 15 May 2006
|
||
+ */
|
||
+
|
||
+#ifndef DIS_ASM_H
|
||
+#define DIS_ASM_H
|
||
+
|
||
+#ifdef __cplusplus
|
||
+extern "C" {
|
||
+#endif
|
||
+
|
||
+#ifdef __KERNEL__
|
||
+#include <asm/ansidecl.h>
|
||
+#include <asm/bfd.h>
|
||
+typedef void FILE;
|
||
+#else /* __KERNEL__ */
|
||
+#include <stdio.h>
|
||
+#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 <eranian@hpl.hp.com>
|
||
+ */
|
||
+
|
||
+#include <linux/init.h>
|
||
+#include <linux/sched.h>
|
||
+#include <asm/atomic.h>
|
||
+
|
||
+#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 <asm/kdb.h>
|
||
+#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 <linux/usb.h>
|
||
+
|
||
+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 <linux/dis-asm.h>
|
||
+#include <asm/kdbprivate.h>
|
||
+#include <asm/bfd.h>
|
||
+
|
||
+ /*
|
||
+ * 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 <linux/swap.h>
|
||
+#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 <linux/usb.h>
|
||
+
|
||
+/* 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 <linux/kdb.h>
|
||
+#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 <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.28-rc6-common-1.
|
||
+
|
||
+2008-11-12 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.28-rc4-common-1.
|
||
+
|
||
+2008-11-04 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * medusa needs kdb to handle '\n' in kdb_read(),
|
||
+ Cliff Wickman <cpw@sgi.com>
|
||
+ * kdb-v4.4-2.6.28-rc3-common-1.
|
||
+
|
||
+2008-10-29 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * "Commandeer vector 0xfe for KDB_VECTOR", version 2.
|
||
+ Cliff Wickman <cpw@sgi.com>
|
||
+ * kdb-v4.4-2.6.28-rc2-common-2.
|
||
+
|
||
+2008-10-27 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.28-rc2-common-1.
|
||
+
|
||
+2008-10-20 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.27-common-1.
|
||
+
|
||
+2008-09-30 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.27-rc8-common-1.
|
||
+
|
||
+2008-09-22 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.27-rc7-common-1.
|
||
+
|
||
+2008-09-03 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.27-rc5-common-1.
|
||
+
|
||
+2008-08-19 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.27-rc3-common-1.
|
||
+
|
||
+2008-08-15 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * mm_online_pgdat_export_symbol, Jay Lan <jlan@sgi.com>
|
||
+ - 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 <jlan@sgi.com>
|
||
+
|
||
+ * 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 <jlan@sgi.com>
|
||
+
|
||
+ * Fix up the NULL pointer deference issue in ohci_kdb_poll_char,
|
||
+ Jason Xiao <jidong.xiao@gmail.com>
|
||
+ * kdb-v4.4-2.6.27-rc2-common-1.
|
||
+
|
||
+2008-07-18 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * support Hardware Breakpoint (bph/bpha) commands
|
||
+ IA64: Greg Banks <gnb@sgi.com>
|
||
+ X86: Konstantin Baydarov <kbaidarov@ru.mvista.com>
|
||
+ * kdb-v4.4-2.6.26-common-2.
|
||
+
|
||
+2008-07-14 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.26-common-1.
|
||
+
|
||
+2008-07-11 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * New commands and some fixups and enhancements,
|
||
+ Joe Korty <joe.korty@ccur.com>
|
||
+ John Blackwood <john.blackwood@ccur.com>
|
||
+ Jim Houston <jim.houston@ccur.com>
|
||
+ - 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 <jlan@sgi.com>
|
||
+
|
||
+ * compilation warning cleanup, Cliff Wickman <cpw@sgi.com>
|
||
+ * kdb-v4.4-2.6.26-rc8-common-1.
|
||
+
|
||
+2008-06-25 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * Added John Blackwood <john.blackwood@ccur.com> 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 <jlan@sgi.com>
|
||
+
|
||
+ * support lcrash style debug_info file: Cliff Wickman <cpw@sgi.com>
|
||
+ - 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 <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.26-rc5-common-1.
|
||
+
|
||
+2008-06-05 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * fixed 'rq/rqa' command runs off the end of runqueue's rt.active
|
||
+ priority bitmap array, John Blackwood <john.blackwood@ccur.com> &
|
||
+ Lachlan McIlroy <lachlan@sgi.com>
|
||
+ * kdb-v4.4-2.6.26-rc4-common-2.
|
||
+
|
||
+2008-05-30 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.26-rc4-common-1.
|
||
+
|
||
+2008-05-20 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.26-rc3-common-1.
|
||
+
|
||
+2008-05-13 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * 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 <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.25-common-1.
|
||
+
|
||
+2008-03-16 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.25-rc6-common-1.
|
||
+
|
||
+2008-03-03 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.25-rc3-common-1.
|
||
+
|
||
+2008-02-26 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * remove 'fastcall' from kdb code.
|
||
+ * kdb-v4.4-2.6.25-rc2-common-1.
|
||
+
|
||
+2008-02-19 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.25-rc1-common-1.
|
||
+
|
||
+2008-02-06 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * 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 <ayoung@sgi.com>
|
||
+ * kdb-v4.4-2.6.24-common-2.
|
||
+
|
||
+2008-01-29 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.24-common-1.
|
||
+
|
||
+2008-01-22 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * USB UHCI kdb support, Konstantin Baydarov <kbaidarov@ru.mvista.com>
|
||
+ * kdb-v4.4-2.6.24-rc8-common-3.
|
||
+
|
||
+2008-01-18 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * USB EHCI kdb support, Aaron Young <ayoung@sgi.com>
|
||
+ * kdb-v4.4-2.6.24-rc8-common-2.
|
||
+
|
||
+2008-01-18 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.24-rc8-common-1.
|
||
+
|
||
+2008-01-07 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb-v4.4-2.6.24-rc7-common-1.
|
||
+
|
||
+2007-12-21 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * 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 <jlan@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.24-rc5-common-1.
|
||
+
|
||
+2007-12-05 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * Fixed a 'sysctl table check failed' problem.
|
||
+ * kdb v4.4-2.6.24-rc4-common-1.
|
||
+
|
||
+2007-11-26 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.24-rc3-common-1.
|
||
+
|
||
+2007-11-13 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * 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 <jlan@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.24-rc2-common-1.
|
||
+
|
||
+2007-11-09 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * 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 <jlan@sgi.com>
|
||
+
|
||
+ * New KDB USB interface, Aaron Young <ayoung@sgi.com>
|
||
+ * 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 <jlan@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.23-common-1.
|
||
+
|
||
+2007-09-26 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.23-rc8-common-1.
|
||
+
|
||
+2007-09-21 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.23-rc7-common-1.
|
||
+
|
||
+2007-09-12 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.23-rc6-common-1.
|
||
+
|
||
+2007-09-06 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.23-rc5-common-1.
|
||
+
|
||
+2007-08-30 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <jlan@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.23-rc4-common-1.
|
||
+
|
||
+2007-08-28 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.23-rc3-common-1.
|
||
+
|
||
+2007-08-24 Jay Lan <jlan@sgi.com>
|
||
+
|
||
+ * 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 <jlan@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.23-rc2-common-1.
|
||
+
|
||
+2007-08-03 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.23-rc1-common-1.
|
||
+
|
||
+2007-07-26 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * New x86 backtrace code.
|
||
+ * kdb v4.4-2.6.22-common-4.
|
||
+
|
||
+2007-07-17 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Make kdb_printf_lock an irq lock to keep lockdep happy.
|
||
+ * kdb v4.4-2.6.22-common-3.
|
||
+
|
||
+2007-07-13 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.22-common-1.
|
||
+
|
||
+2007-07-02 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.22-rc7-common-1.
|
||
+
|
||
+2007-06-20 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.22-rc5-common-1.
|
||
+
|
||
+2007-06-15 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.22-rc4-common-1.
|
||
+
|
||
+2007-05-28 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.22-rc3-common-1.
|
||
+
|
||
+2007-05-22 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.22-rc2-common-1.
|
||
+
|
||
+2007-05-22 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.22-rc1-common-1.
|
||
+
|
||
+2007-05-17 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Correct alignment of debug_alloc_header.
|
||
+ * kdb v4.4-2.6.21-common-2.
|
||
+
|
||
+2007-04-29 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.21-common-1.
|
||
+
|
||
+2007-04-16 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Remove dead symbol declarations.
|
||
+ * kdb v4.4-2.6.21-rc7-common-2.
|
||
+
|
||
+2007-04-16 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.21-rc7-common-1.
|
||
+
|
||
+2007-04-10 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.21-rc6-common-1.
|
||
+
|
||
+2007-04-02 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.21-rc5-common-1.
|
||
+
|
||
+2007-03-19 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.21-rc4-common-1.
|
||
+
|
||
+2007-03-14 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.21-rc3-common-1.
|
||
+
|
||
+2007-03-14 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.21-rc2-common-1.
|
||
+
|
||
+2007-03-01 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.21-rc1-common-1.
|
||
+
|
||
+2007-03-01 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Remove sparse warnings.
|
||
+ * kdb v4.4-2.6.20-common-6.
|
||
+
|
||
+2007-02-27 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Initialise struct disassemble_info in kdb_id1().
|
||
+ * kdb v4.4-2.6.20-common-4.
|
||
+
|
||
+2007-02-16 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Clean up debug_alloc_pool code.
|
||
+ * kdb v4.4-2.6.20-common-3.
|
||
+
|
||
+2007-02-16 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Initialise variable bits of struct disassemble_info each time.
|
||
+ * kdb v4.4-2.6.20-common-2.
|
||
+
|
||
+2007-02-06 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.20-common-1.
|
||
+
|
||
+2007-02-01 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.20-rc7-common-1.
|
||
+
|
||
+2007-01-08 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.20-rc4-common-1.
|
||
+
|
||
+2007-01-02 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.20-rc3-common-1.
|
||
+
|
||
+2006-12-21 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.20-rc1-common-1.
|
||
+
|
||
+2006-11-30 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.19-common-1.
|
||
+
|
||
+2006-11-30 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Do not access registers if kdb_current_regs is NULL.
|
||
+ * kdb v4.4-2.6.19-rc6-common-3.
|
||
+
|
||
+2006-11-27 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.19-rc6-common-1.
|
||
+
|
||
+2006-11-09 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.19-rc5-common-1.
|
||
+
|
||
+2006-11-01 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.19-rc4-common-1.
|
||
+
|
||
+2006-10-24 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.19-rc3-common-1.
|
||
+
|
||
+2006-10-24 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Remove redundant regs and envp parameters.
|
||
+ * kdb v4.4-2.6.19-rc2-common-2.
|
||
+
|
||
+2006-10-18 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.19-rc2-common-1.
|
||
+
|
||
+2006-10-11 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.19-rc1-common-1.
|
||
+
|
||
+2006-10-06 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Remove #include <linux/config.h>
|
||
+ * kdb v4.4-2.6.18-common-2.
|
||
+
|
||
+2006-09-20 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.18-common-1.
|
||
+
|
||
+2006-09-15 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.18-rc7-common-1.
|
||
+
|
||
+2006-08-29 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Rewrite all backtrace code.
|
||
+ * kdb v4.4-2.6.18-rc5-common-2.
|
||
+
|
||
+2006-08-28 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.18-rc5-common-1.
|
||
+
|
||
+2006-08-08 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.18-rc4-common-1.
|
||
+
|
||
+2006-08-04 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.18-rc3-common-1.
|
||
+
|
||
+2006-07-18 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.18-rc2-common-1.
|
||
+
|
||
+2006-07-12 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.18-rc1-common-1.
|
||
+
|
||
+2006-07-04 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.17-common-1.
|
||
+
|
||
+2006-05-31 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.17-rc5-common-1.
|
||
+
|
||
+2006-05-15 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.17-rc4-common-1.
|
||
+
|
||
+2006-04-28 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.17-rc3-common-1.
|
||
+
|
||
+2006-04-22 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.17-rc2-common-1.
|
||
+
|
||
+2006-04-11 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.17-rc1-common-1.
|
||
+
|
||
+2006-04-05 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * More fixes for the timing race with KDB_ENTER_SLAVE.
|
||
+ * kdb v4.4-2.6.16-common-5.
|
||
+
|
||
+2006-03-30 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Change CONFIG_LKCD to CONFIG_LKCD_DUMP.
|
||
+ * kdb v4.4-2.6.16-common-3.
|
||
+
|
||
+2006-03-22 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.16-common-1.
|
||
+
|
||
+2006-03-14 Nathan Scott <nathans@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.16-rc6-common-1.
|
||
+
|
||
+2006-02-28 Nathan Scott <nathans@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.16-rc5-common-1.
|
||
+
|
||
+2006-02-20 Nathan Scott <nathans@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.16-rc4-common-1.
|
||
+
|
||
+2006-02-06 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Add xpcusers command. Dean Nelson, SGI.
|
||
+ * kdb v4.4-2.6.16-rc2-common-1.
|
||
+
|
||
+2006-02-02 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.16-rc1-common-1.
|
||
+
|
||
+2006-01-11 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.15-rc7-common-1.
|
||
+
|
||
+2005-12-20 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.15-rc6-common-1.
|
||
+
|
||
+2005-12-10 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.15-rc5-common-1.
|
||
+
|
||
+2005-12-02 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * New follow_page() API.
|
||
+ * kdb v4.4-2.6.15-rc3-common-1.
|
||
+
|
||
+2005-11-21 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.15-rc2-common-1.
|
||
+
|
||
+2005-11-15 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.15-rc1-common-1.
|
||
+
|
||
+2005-11-15 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.14-common-1.
|
||
+
|
||
+2005-10-21 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.14-rc5-common-1.
|
||
+
|
||
+2005-10-11 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Handle removal of USB keyboard. Aaron Young, SGI.
|
||
+ * kdb v4.4-2.6.14-rc4-common-1.
|
||
+
|
||
+2005-10-05 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.13-common-1.
|
||
+
|
||
+2005-08-24 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.13-rc7-common-1.
|
||
+
|
||
+2005-08-08 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.13-rc6-common-1.
|
||
+
|
||
+2005-08-02 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.13-rc4-common-1.
|
||
+
|
||
+2005-07-26 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Fix compile problem with CONFIG_USB_KBD.
|
||
+ * kdb v4.4-2.6.13-rc3-common-3.
|
||
+
|
||
+2005-07-22 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * The asmlinkage kdb() patch was lost during packaging. Reinstate it.
|
||
+ * kdb v4.4-2.6.13-rc3-common-2.
|
||
+
|
||
+2005-07-19 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Add support for USB keyboard (OHCI only). Aaron Young, SGI.
|
||
+ * kdb v4.4-2.6.13-rc3-common-1.
|
||
+
|
||
+2005-07-08 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.13-rc2-common-1.
|
||
+
|
||
+2005-07-01 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.12-common-1.
|
||
+
|
||
+2005-06-08 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Correct early exit from bd *.
|
||
+ * kdb v4.4-2.6.12-rc6-common-1.
|
||
+
|
||
+2005-05-25 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.12-rc4-common-1.
|
||
+
|
||
+2005-04-21 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Add rpte command (find the pte for a physical page).
|
||
+ * kdb v4.4-2.6.12-rc3-common-1.
|
||
+
|
||
+2005-04-06 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Add rq and rqa commands. John Hawkes, SGI.
|
||
+ * kdb v4.4-2.6.12-rc2-common-1.
|
||
+
|
||
+2005-03-29 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * More coexistence patches for lkcd. Jason Uhlenkott, SGI.
|
||
+ * kdb v4.4-2.6.11-common-3.
|
||
+
|
||
+2005-03-08 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Coexistence patches for lkcd. Jason Uhlenkott, SGI.
|
||
+ * kdb v4.4-2.6.11-common-2.
|
||
+
|
||
+2005-03-03 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Add kdb to drivers/serial/8250_early.c. Francois Wellenreiter, Bull.
|
||
+ * kdb v4.4-2.6.11-common-1.
|
||
+
|
||
+2005-02-14 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.11-rc4-common-1.
|
||
+
|
||
+2005-02-08 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.11-rc3-bk4-common-1.
|
||
+
|
||
+2005-02-03 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Add bio command. Nathan Scott, SGI.
|
||
+ * kdb v4.4-2.6.11-rc2-common-1.
|
||
+
|
||
+2005-01-20 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Include kallsyms correction for modules until Linus takes it.
|
||
+ * kdb v4.4-2.6.11-rc1-bk7-common-1.
|
||
+
|
||
+2005-01-12 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.10-rc3-common-1.
|
||
+
|
||
+2004-11-23 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Build with kdb patch applied but CONFIG_KDB=n.
|
||
+ * kdb v4.4-2.6.10-rc1-common-2.
|
||
+
|
||
+2004-10-29 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Handle variable size for the kernel log buffer.
|
||
+ * kdb v4.4-2.6.9-common-2.
|
||
+
|
||
+2004-10-19 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.9-common-1.
|
||
+
|
||
+2004-10-12 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.9-rc4-common-1.
|
||
+
|
||
+2004-10-01 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.9-rc3-common-1.
|
||
+
|
||
+2004-09-30 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Add task states C (traCed) and E (dEad).
|
||
+ * kdb v4.4-2.6.9-rc2-common-1.
|
||
+
|
||
+2004-08-27 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.9-rc1-common-1.
|
||
+
|
||
+2004-08-14 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.8-common-1.
|
||
+
|
||
+2004-08-12 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.8-rc4-common-1.
|
||
+
|
||
+2004-08-05 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Mark kdb_initcall as __attribute_used__ for newer gcc.
|
||
+ * kdb v4.4-2.6.8-rc3-common-2.
|
||
+
|
||
+2004-08-04 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Add mdp (memory display physical) comnmand.
|
||
+ Ananth N Mavinakayanahalli, IBM.
|
||
+ * kdb v4.4-2.6.8-rc3-common-1.
|
||
+
|
||
+2004-07-18 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Patch for new sn_console. Erik Jacobson. SGI.
|
||
+ * kdb v4.4-2.6.8-rc2-common-1.
|
||
+
|
||
+2004-07-12 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Common changes to help the x86-64 port.
|
||
+ * kdb v4.4-2.6.7-common-3.
|
||
+
|
||
+2004-06-20 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.7-common-1.
|
||
+
|
||
+2004-06-09 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.7-rc3-common-1.
|
||
+
|
||
+2004-06-09 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Whitespace clean up, no code changes.
|
||
+ * kdb v4.4-2.6.7-rc2-common-2.
|
||
+
|
||
+2004-06-07 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.4-2.6.7-rc2-common-1.
|
||
+
|
||
+2004-06-06 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <esc>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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.3-2.6.6-common-1.
|
||
+
|
||
+2004-05-06 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.3-2.6.6-rc3-common-1.
|
||
+
|
||
+2004-05-06 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.3-2.6.6-rc2-common-1.
|
||
+
|
||
+2004-04-30 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Unlock sn_sal_lock before entering kdb from sn_serial.
|
||
+ * kdb v4.3-2.6.5-common-2.
|
||
+
|
||
+2004-04-05 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.3-2.6.5-common-1.
|
||
+
|
||
+2004-03-22 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.3-2.6.5-rc2-common-1.
|
||
+
|
||
+2004-03-12 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.3-2.6.4-rc1-common-1.
|
||
+
|
||
+2004-02-21 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.3-2.6.3-common-1.
|
||
+
|
||
+2004-02-17 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Remove WAR for incorrect console registration patch.
|
||
+ * kdb v4.3-2.6.3-rc4-common-1.
|
||
+
|
||
+2004-02-17 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <xavier.bru@bull.net>
|
||
+ * Merge to 2.6.0-test9
|
||
+2003-10-17 Xavier Bru <xavier.bru@bull.net>
|
||
+ * fix NUll ptr in kdb_ps at early prompt.
|
||
+2003-10-14 Xavier Bru <xavier.bru@bull.net>
|
||
+ * fix NUll ptr in kdb_ps when cpu not present.
|
||
+2003-10-06 Xavier Bru <xavier.bru@bull.net>
|
||
+ * Merge to 2.6.0-test5
|
||
+ * fix compile error with CONFIG_MODULES not set.
|
||
+
|
||
+2003-09-08 Xavier Bru <xavier.bru@bull.net>
|
||
+ * Merge to 2.6.0-test4
|
||
+
|
||
+2003-07-10 Xavier Bru <xavier.bru@bull.net>
|
||
+
|
||
+ * Merge kdb v4.3 to 2.5.72 ia64
|
||
+ * don't call local_bh_enable() with interrupts masked.
|
||
+
|
||
+2003-04-07 Xavier Bru <xavier.bru@bull.net>
|
||
+
|
||
+ * Merge kdb v4.1 to 2.5.64 ia64
|
||
+ * new kernel parameters support
|
||
+ * new module format
|
||
+ * new kallsyms support
|
||
+
|
||
+2003-12-02 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.3-2.4.23-common-1.
|
||
+
|
||
+2003-11-11 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Sync with XFS 2.4.22 tree.
|
||
+ * kdb v4.3-2.4.22-common-2.
|
||
+
|
||
+2003-08-29 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.3-2.4.22-common-1.
|
||
+
|
||
+2003-07-27 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.3-2.4.22-pre8-common-8.
|
||
+
|
||
+2003-07-20 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Correct ll command.
|
||
+ * kdb v4.3-2.4.21-common-7.
|
||
+
|
||
+2003-07-08 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Export more kdb symbols. Vamsi Krishna S., IBM.
|
||
+ * kdb v4.3-2.4.21-common-6.
|
||
+
|
||
+2003-07-07 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Tweak 'waiting for cpus' message.
|
||
+ * kdb v4.3-2.4.21-common-5.
|
||
+
|
||
+2003-07-07 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Add task and sigset commands. Mark Goodwin, SGI.
|
||
+ * kdb v4.3-2.4.21-common-3.
|
||
+
|
||
+2003-06-23 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Sync with XFS 2.4.21 tree.
|
||
+ * kdb v4.3-2.4.21-common-2.
|
||
+
|
||
+2003-06-20 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * kdb v4.3-2.4.21-common-1.
|
||
+
|
||
+2003-06-20 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <cpuid> 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Upgrade to 2.4.20-rc1.
|
||
+ * kdb v2.5-2.4.20-rc1-common-1.
|
||
+
|
||
+2002-11-14 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Fix processing with O(1) scheduler.
|
||
+ * 'go' switches back to initial cpu first.
|
||
+ * 'go <address>' 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 <kaos@sgi.com>
|
||
+
|
||
+ * Correct build without CONFIG_KDB.
|
||
+ * kdb v2.4-2.4.19-common-3.
|
||
+
|
||
+2002-11-01 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Minimize differences from 2.5.44.
|
||
+ * kdb v2.4-2.4.19-common-2.
|
||
+
|
||
+2002-10-31 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Add selection critera to ps and bta commands.
|
||
+ * kdb v2.3-2.4.19-common-4.
|
||
+
|
||
+2002-10-07 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * New man page, Documentation/kdb/kdb_sr.man.
|
||
+
|
||
+2002-10-04 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Upgrade to 2.4.19-pre10.
|
||
+ * Sync with XFS.
|
||
+ * kdb v2.1-2.4.19-pre10-common-1.
|
||
+
|
||
+2002-04-09 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Upgrade to 2.4.19-pre6.
|
||
+ * kdb v2.1-2.4.19-pre6-common-1.
|
||
+
|
||
+2002-03-18 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Syntax check mdWcN commands.
|
||
+
|
||
+2002-03-01 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Sync with XFS 2.4.18.
|
||
+ * kdb v2.1-2.4.18-common-2.
|
||
+
|
||
+2002-02-26 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Sync with XFS pagebuf changes.
|
||
+ * kdb v2.1-2.4.17-common-2.
|
||
+
|
||
+2002-01-18 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <count>, 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Sync xfs <-> kdb common code.
|
||
+
|
||
+2001-12-22 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Upgrade to 2.4.15-pre5.
|
||
+ * Wrap () around #define expressions with unary operators.
|
||
+
|
||
+2001-11-13 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Upgrade to 2.4.15-pre4.
|
||
+ * kbdm_pg.c patch from Hugh Dickins.
|
||
+
|
||
+2001-11-07 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * 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 <kaos@sgi.com>
|
||
+
|
||
+ * Upgrade to kernel 2.4.14.
|
||
+
|
||
+2001-11-02 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Sync kdbm_pg.c with XFS.
|
||
+
|
||
+2001-10-24 Keith Owens <kaos@sgi.com>
|
||
+
|
||
+ * Upgrade to kernel 2.4.13.
|
||
+
|
||
+2001-10-14 Keith Owens <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * Update kdb v1.8 to kernel 2.4.2, sync kdb/modules with XFS.
|
||
+
|
||
+ * Hook into panic() call.
|
||
+
|
||
+2000-12-18 Keith Owens <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * Update kdb v1.7 to kernel 2.4.0-test13-pre3, sync kdb/modules with
|
||
+ XFS.
|
||
+
|
||
+2000-11-18 Keith Owens <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * Update to kernel 2.4.0-test10-pre4.
|
||
+
|
||
+2000-10-15 Keith Owens <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * kdb/kdbmain.c: Reduce CMD_LEN to avoid overflowing kdb_printf buffer.
|
||
+
|
||
+2000-10-11 Keith Owens <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <linux/kdb.h> to appear in arch independent
|
||
+ code without causing compile errors.
|
||
+
|
||
+ * kdb/modules/kdbm_pg: Sync with XFS.
|
||
+
|
||
+2000-10-03 Keith Owens <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <quintela@fi.udc.es>
|
||
+
|
||
+ * kdb/arch/i386/kdb/kdba_io.c: Allow kdb to compile without CONFIG_VT and/or
|
||
+ serial console.
|
||
+
|
||
+2000-09-19 Keith Owens <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <kaos@melbourne.sgi.com>
|
||
+
|
||
+ * 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 <linux/stddef.h>"; print "\#include <linux/init.h>"} \
|
||
+ /^\#/{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 <linux/string.h>
|
||
+#include <linux/kernel.h>
|
||
+#include <linux/init.h>
|
||
+#include <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+#include <linux/smp.h>
|
||
+#include <linux/sched.h>
|
||
+#include <linux/interrupt.h>
|
||
+#include <asm/system.h>
|
||
+
|
||
+/*
|
||
+ * 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; i<KDB_MAXBPT; i++) {
|
||
+ kdb_bp_t *bp = &kdb_breakpoints[i];
|
||
+
|
||
+ if (KDB_DEBUG(BP)) {
|
||
+ kdb_printf("kdb_bp_install_global bp %d bp_enabled %d bp_global %d\n",
|
||
+ i, bp->bp_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; i<KDB_MAXBPT; i++) {
|
||
+ kdb_bp_t *bp = &kdb_breakpoints[i];
|
||
+
|
||
+ if (KDB_DEBUG(BP)) {
|
||
+ kdb_printf("kdb_bp_install_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_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] <addr-expression> [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; bpno<KDB_MAXBPT; bpno++, bp++) {
|
||
+ if (bp->bp_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; bpno<KDB_MAXBPT; bpno++,bp++) {
|
||
+ if (bp->bp_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; i<KDB_MAXBPT; i++,bp_check++) {
|
||
+ if (!bp_check->bp_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] <breakpoint-number>
|
||
+ * [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; i<KDB_MAXBPT; i++, bp++) {
|
||
+ if (bp->bp_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; i<KDB_MAXBPT; i++, bp++) {
|
||
+ bp->bp_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, "[<vaddr>]", "Set/Display breakpoints", 0, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("bl", kdb_bp, "[<vaddr>]", "Display breakpoints", 0, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("bpa", kdb_bp, "[<vaddr>]", "Set/Display global breakpoints", 0, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("bph", kdb_bp, "[<vaddr>]", "Set hardware breakpoint", 0, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("bpha", kdb_bp, "[<vaddr>]", "Set global hardware breakpoint", 0, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("bc", kdb_bc, "<bpnum>", "Clear Breakpoint", 0, KDB_REPEAT_NONE);
|
||
+ kdb_register_repeat("be", kdb_bc, "<bpnum>", "Enable Breakpoint", 0, KDB_REPEAT_NONE);
|
||
+ kdb_register_repeat("bd", kdb_bc, "<bpnum>", "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 <linux/ctype.h>
|
||
+#include <linux/string.h>
|
||
+#include <linux/kernel.h>
|
||
+#include <linux/sched.h>
|
||
+#include <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+#include <linux/nmi.h>
|
||
+#include <asm/system.h>
|
||
+
|
||
+
|
||
+/*
|
||
+ * kdb_bt
|
||
+ *
|
||
+ * This function implements the 'bt' command. Print a stack
|
||
+ * traceback.
|
||
+ *
|
||
+ * bt [<address-expression>] (addr-exp is for alternate stacks)
|
||
+ * btp <pid> Kernel stack for <pid>
|
||
+ * btt <address-expression> Kernel stack for task structure at <address-expression>
|
||
+ * bta [DRSTCZEUIMA] All useful processes, optionally filtered by state
|
||
+ * btc [<cpu>] The current process on one cpu, default is all cpus
|
||
+ *
|
||
+ * bt <address-expression> refers to a address on the stack, that location
|
||
+ * is assumed to contain a return address.
|
||
+ *
|
||
+ * btt <address-expression> 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 <address-expression>.
|
||
+ */
|
||
+
|
||
+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 <q> to end, <cr> 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 <stdarg.h>
|
||
+#include <linux/kernel.h>
|
||
+#include <linux/init.h>
|
||
+#include <linux/ctype.h>
|
||
+#include <linux/string.h>
|
||
+#include <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+
|
||
+disassemble_info kdb_di;
|
||
+
|
||
+/*
|
||
+ * kdb_id
|
||
+ *
|
||
+ * Handle the id (instruction display) command.
|
||
+ *
|
||
+ * id [<addr>]
|
||
+ *
|
||
+ * 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<icount; i++) {
|
||
+ pc += kdba_id_printinsn(pc, &kdb_di);
|
||
+ kdb_printf("\n");
|
||
+ }
|
||
+
|
||
+ lastpc = pc;
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+/*
|
||
+ * kdb_id1
|
||
+ *
|
||
+ * Disassemble a single instruction at 'pc'.
|
||
+ *
|
||
+ * Parameters:
|
||
+ * pc Address of instruction to disassemble
|
||
+ * Outputs:
|
||
+ * None.
|
||
+ * Returns:
|
||
+ * Zero for success, a kdb diagnostic if failure.
|
||
+ * Locking:
|
||
+ * None.
|
||
+ * Remarks:
|
||
+ */
|
||
+
|
||
+void
|
||
+kdb_id1(unsigned long pc)
|
||
+{
|
||
+ char *mode;
|
||
+ int diag;
|
||
+
|
||
+ kdb_di.fprintf_func = kdb_dis_fprintf;
|
||
+ kdba_id_init(&kdb_di);
|
||
+
|
||
+ /*
|
||
+ * Allow the user to specify that this instruction
|
||
+ * should be treated differently.
|
||
+ */
|
||
+
|
||
+ mode = kdbgetenv("IDMODE");
|
||
+ diag = kdba_id_parsemode(mode, &kdb_di);
|
||
+ if (diag) {
|
||
+ kdb_printf("kdb_id: bad value in 'IDMODE' environment variable ignored\n");
|
||
+ }
|
||
+
|
||
+ (void) kdba_id_printinsn(pc, &kdb_di);
|
||
+ kdb_printf("\n");
|
||
+}
|
||
+
|
||
+/*
|
||
+ * kdb_dis_fprintf
|
||
+ *
|
||
+ * Format and print a string.
|
||
+ *
|
||
+ * Parameters:
|
||
+ * file Unused paramter.
|
||
+ * fmt Format string
|
||
+ * ... Optional additional parameters.
|
||
+ * Returns:
|
||
+ * 0
|
||
+ * Locking:
|
||
+ * Remarks:
|
||
+ * Result of format conversion cannot exceed 255 bytes.
|
||
+ */
|
||
+
|
||
+int
|
||
+kdb_dis_fprintf(PTR file, const char *fmt, ...)
|
||
+{
|
||
+ char buffer[256];
|
||
+ va_list ap;
|
||
+
|
||
+ va_start(ap, fmt);
|
||
+ vsprintf(buffer, fmt, ap);
|
||
+ va_end(ap);
|
||
+
|
||
+ kdb_printf("%s", buffer);
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+/*
|
||
+ * kdb_dis_fprintf_dummy
|
||
+ *
|
||
+ * A dummy printf function for the disassembler, it does nothing.
|
||
+ * This lets code call the disassembler to step through
|
||
+ * instructions without actually printing anything.
|
||
+ * Inputs:
|
||
+ * Always ignored.
|
||
+ * Outputs:
|
||
+ * None.
|
||
+ * Returns:
|
||
+ * Always 0.
|
||
+ * Locking:
|
||
+ * none.
|
||
+ * Remarks:
|
||
+ * None.
|
||
+ */
|
||
+
|
||
+int
|
||
+kdb_dis_fprintf_dummy(PTR file, const char *fmt, ...)
|
||
+{
|
||
+ return(0);
|
||
+}
|
||
+
|
||
+/*
|
||
+ * kdb_disinit
|
||
+ *
|
||
+ * Initialize the disassembly information structure
|
||
+ * for the GNU disassembler.
|
||
+ *
|
||
+ * Parameters:
|
||
+ * None.
|
||
+ * Outputs:
|
||
+ * None.
|
||
+ * Returns:
|
||
+ * Zero for success, a kdb diagnostic if failure.
|
||
+ * Locking:
|
||
+ * None.
|
||
+ * Remarks:
|
||
+ */
|
||
+
|
||
+void __init
|
||
+kdb_id_init(void)
|
||
+{
|
||
+ kdb_di.stream = NULL;
|
||
+ kdb_di.application_data = NULL;
|
||
+ kdb_di.symbols = NULL;
|
||
+ kdb_di.num_symbols = 0;
|
||
+ kdb_di.flags = 0;
|
||
+ kdb_di.private_data = NULL;
|
||
+ kdb_di.buffer = NULL;
|
||
+ kdb_di.buffer_vma = 0;
|
||
+ kdb_di.buffer_length = 0;
|
||
+ kdb_di.bytes_per_line = 0;
|
||
+ kdb_di.bytes_per_chunk = 0;
|
||
+ kdb_di.insn_info_valid = 0;
|
||
+ kdb_di.branch_delay_insns = 0;
|
||
+ kdb_di.data_size = 0;
|
||
+ kdb_di.insn_type = 0;
|
||
+ kdb_di.target = 0;
|
||
+ kdb_di.target2 = 0;
|
||
+}
|
||
--- /dev/null
|
||
+++ b/kdb/kdb_io.c
|
||
@@ -0,0 +1,859 @@
|
||
+/*
|
||
+ * Kernel Debugger Architecture Independent Console I/O 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-2006 Silicon Graphics, Inc. All Rights Reserved.
|
||
+ */
|
||
+
|
||
+#include <linux/module.h>
|
||
+#include <linux/types.h>
|
||
+#include <linux/ctype.h>
|
||
+#include <linux/kernel.h>
|
||
+#include <linux/init.h>
|
||
+#include <linux/kdev_t.h>
|
||
+#include <linux/console.h>
|
||
+#include <linux/string.h>
|
||
+#include <linux/sched.h>
|
||
+#include <linux/smp.h>
|
||
+#include <linux/nmi.h>
|
||
+#include <linux/delay.h>
|
||
+
|
||
+#include <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+#include <linux/kallsyms.h>
|
||
+
|
||
+static struct console *kdbcons;
|
||
+
|
||
+#ifdef CONFIG_PPC64
|
||
+#include <asm/udbg.h>
|
||
+#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<something> */
|
||
+ if (key != '[')
|
||
+ escape_delay = 2;
|
||
+ continue;
|
||
+ } else if (ped - escape_data == 3) {
|
||
+ /* \e[<something> */
|
||
+ 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><something> */
|
||
+ 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<count;i++) {
|
||
+ if(kallsyms_symbol_next(p_tmp, i)<0)
|
||
+ break;
|
||
+ kdb_printf("%s ",p_tmp);
|
||
+ *(p_tmp+len)='\0';
|
||
+ }
|
||
+ if(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 <linux/kernel.h>
|
||
+#include <linux/module.h>
|
||
+#include <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+#include <linux/fs.h>
|
||
+#include <asm/processor.h>
|
||
+#include <asm/uaccess.h>
|
||
+#include <asm/fcntl.h>
|
||
+#include <linux/vmalloc.h>
|
||
+#include <linux/ctype.h>
|
||
+#include <linux/file.h>
|
||
+#include <linux/err.h>
|
||
+#include <linux/slab.h>
|
||
+#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 <expression>
|
||
+ * e.g. px *(task_struct *) <address>
|
||
+ */
|
||
+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 <symbol | type>\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("<UNKNOWN>");
|
||
+ } else {
|
||
+ kdb_printf( "typedef <UNKNOWN>%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("<UNKNOWN>");
|
||
+ }
|
||
+ }
|
||
+ 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("<ARRAY_TYPE>");
|
||
+ PRINT_SEMI_COLON(level, flags);
|
||
+ PRINT_NL(flags);
|
||
+ return;
|
||
+ }
|
||
+ } else {
|
||
+ rkltp = kltp;
|
||
+ }
|
||
+
|
||
+ etp = rkltp->kl_elementtype;
|
||
+ if (!etp) {
|
||
+ LEVEL_INDENT(level, flags);
|
||
+ kdb_printf("<BAD_ELEMENT_TYPE> %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("<VOID>");
|
||
+ } 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("<UNKNOWN> {\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("<INCOMPLETE TYPE>\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 <linux/types.h>
|
||
+#include <asm/div64.h>
|
||
+/*
|
||
+ * 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 <eranian@hpl.hp.com>
|
||
+ * Xscale (R) modifications copyright (C) 2003 Intel Corporation.
|
||
+ */
|
||
+
|
||
+/*
|
||
+ * Updated for Xscale (R) architecture support
|
||
+ * Eddie Dong <eddie.dong@intel.com> 8 Jan 03
|
||
+ */
|
||
+
|
||
+#include <linux/ctype.h>
|
||
+#include <linux/string.h>
|
||
+#include <linux/kernel.h>
|
||
+#include <linux/reboot.h>
|
||
+#include <linux/sched.h>
|
||
+#include <linux/sysrq.h>
|
||
+#include <linux/smp.h>
|
||
+#include <linux/utsname.h>
|
||
+#include <linux/vmalloc.h>
|
||
+#include <linux/module.h>
|
||
+#include <linux/mm.h>
|
||
+#include <linux/init.h>
|
||
+#include <linux/kallsyms.h>
|
||
+#include <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+#include <linux/notifier.h>
|
||
+#include <linux/interrupt.h>
|
||
+#include <linux/delay.h>
|
||
+#include <linux/nmi.h>
|
||
+#include <linux/ptrace.h>
|
||
+#include <linux/sysctl.h>
|
||
+#if defined(CONFIG_LKCD_DUMP) || defined(CONFIG_LKCD_DUMP_MODULE)
|
||
+#include <linux/dump.h>
|
||
+#endif
|
||
+#include <linux/cpu.h>
|
||
+#include <linux/kdebug.h>
|
||
+#ifdef CONFIG_KDB_KDUMP
|
||
+#include <linux/kexec.h>
|
||
+#endif
|
||
+
|
||
+#include <acpi/acpi_bus.h>
|
||
+
|
||
+#include <asm/system.h>
|
||
+#include <asm/kdebug.h>
|
||
+#include <linux/proc_fs.h>
|
||
+#include <asm/uaccess.h>
|
||
+#include <linux/slab.h>
|
||
+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 <addr arg>,<byte count>
|
||
+ *
|
||
+ * 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 [<addr arg> [<line count> [<radix>]]]
|
||
+ * mdWcN [<addr arg> [<line count> [<radix>]]]
|
||
+ * 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 <addr arg>,<byte count>
|
||
+ *
|
||
+ * 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 <addr>' 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 <address> 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 <magic-sysrq-code>
|
||
+ *
|
||
+ * 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 [<cpunum>]
|
||
+ *
|
||
+ * 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; i++) {
|
||
+ if (cpu_online(i))
|
||
+ KDB_STATE_CLEAR_CPU(KDB, i);
|
||
+ }
|
||
+ KDB_STATE_SET(KDB);
|
||
+ barrier();
|
||
+ /* wait for the other cpus to notice and set state KDB again,
|
||
+ * see kdb_main_loop
|
||
+ */
|
||
+ udelay(1000);
|
||
+
|
||
+ if (argc == 0) {
|
||
+ kdb_cpu_status();
|
||
+ return 0;
|
||
+ }
|
||
+
|
||
+ if (argc != 1)
|
||
+ return KDB_ARGCOUNT;
|
||
+
|
||
+ diag = kdbgetularg(argv[1], &cpunum);
|
||
+ if (diag)
|
||
+ return diag;
|
||
+
|
||
+ /*
|
||
+ * Validate cpunum
|
||
+ */
|
||
+ if ((cpunum > 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 [<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; i<kdb_max_commands; i++, kt++) {
|
||
+ if (kt->cmd_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; i<kdb_max_commands; i++, kp++) {
|
||
+ if (kp->cmd_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; i<kdb_max_commands; i++, kp++) {
|
||
+ if (kp->cmd_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; i<kdb_max_commands; i++, kp++) {
|
||
+ if (kp->cmd_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, "<vaddr>", "Display Memory Contents, also mdWcN, e.g. md8c1", 1, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("mdr", kdb_md, "<vaddr> <bytes>", "Display Raw Memory", 0, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("mdp", kdb_md, "<paddr> <bytes>", "Display Physical Memory", 0, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("mds", kdb_md, "<vaddr>", "Display Memory Symbolically", 0, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("mm", kdb_mm, "<vaddr> <contents>", "Modify Memory Contents", 0, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("id", kdb_id, "<vaddr>", "Display Instructions", 1, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("go", kdb_go, "[<vaddr>]", "Continue Execution", 1, KDB_REPEAT_NONE);
|
||
+ kdb_register_repeat("rd", kdb_rd, "", "Display Registers", 1, KDB_REPEAT_NONE);
|
||
+ kdb_register_repeat("rm", kdb_rm, "<reg> <contents>", "Modify Registers", 0, KDB_REPEAT_NONE);
|
||
+ kdb_register_repeat("ef", kdb_ef, "<vaddr>", "Display exception frame", 0, KDB_REPEAT_NONE);
|
||
+ kdb_register_repeat("bt", kdb_bt, "[<vaddr>]", "Stack traceback", 1, KDB_REPEAT_NONE);
|
||
+ kdb_register_repeat("btp", kdb_bt, "<pid>", "Display stack for process <pid>", 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, "<vaddr>", "Backtrace process given its struct task address", 0, KDB_REPEAT_NONE);
|
||
+ kdb_register_repeat("ll", kdb_ll, "<first-element> <linkoffset> <cmd>", "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, "<cpunum>","Switch to new cpu", 0, KDB_REPEAT_NONE);
|
||
+ kdb_register_repeat("ps", kdb_ps, "[<flags>|A]", "Display active task list", 0, KDB_REPEAT_NONE);
|
||
+ kdb_register_repeat("pid", kdb_pid, "<pidnum>", "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, "<key>", "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> <pid>", "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, "<expression>",
|
||
+ "Type casting, as in lcrash", 0, KDB_REPEAT_NONE);
|
||
+ kdb_register_repeat("px", kdb_debuginfo_print, "<expression>",
|
||
+ "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, "<expression>",
|
||
+ "Print in decimal (type casting)", 0, KDB_REPEAT_NONE);
|
||
+ kdb_register_repeat("whatis", kdb_debuginfo_print,"<type or symbol>",
|
||
+ "Display the type, or the address for a symbol", 0, KDB_REPEAT_NONE);
|
||
+ kdb_register_repeat("sizeof", kdb_debuginfo_print, "<type>",
|
||
+ "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 <xavier.bru@bull.net>
|
||
+ */
|
||
+
|
||
+#include <stdarg.h>
|
||
+#include <linux/types.h>
|
||
+#include <linux/sched.h>
|
||
+#include <linux/mm.h>
|
||
+#include <linux/kallsyms.h>
|
||
+#include <linux/stddef.h>
|
||
+#include <linux/vmalloc.h>
|
||
+#include <linux/ptrace.h>
|
||
+#include <linux/module.h>
|
||
+#include <linux/highmem.h>
|
||
+#include <linux/hardirq.h>
|
||
+#include <linux/delay.h>
|
||
+#include <linux/slab.h>
|
||
+
|
||
+#include <asm/uaccess.h>
|
||
+
|
||
+#include <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+
|
||
+/*
|
||
+ * 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 <linux/kernel.h>
|
||
+#include <linux/module.h>
|
||
+#include <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+#include <linux/fs.h>
|
||
+#include <asm/processor.h>
|
||
+#include <asm/uaccess.h>
|
||
+#include <asm/fcntl.h>
|
||
+#include <linux/vmalloc.h>
|
||
+#include <linux/ctype.h>
|
||
+#include <linux/file.h>
|
||
+#include <linux/err.h>
|
||
+#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; i<num_strings; i++) {
|
||
+ *(stringp_array+i) = ptr;
|
||
+ len = strlen(ptr) + 1;
|
||
+ ptr += len;
|
||
+ }
|
||
+
|
||
+ /* 2) the kltypes */
|
||
+ kltypesection = (char *)(stringsection + section_size);
|
||
+ lp = (long *)kltypesection;
|
||
+ section_size = *lp++;
|
||
+ num_kltypes = *lp++;
|
||
+ element_size = *lp++;
|
||
+ /* sanity check: */
|
||
+ if (element_size != sizeof(kltype_t)) {
|
||
+ printk("size of kltype_t:%ld does not match\n", element_size);
|
||
+ goto bad;
|
||
+ }
|
||
+ kltypestorage = (void *)lp;
|
||
+ kltype_array = (kltype_t *)kltypestorage;
|
||
+
|
||
+ /* 3) the dbg_sym_t's */
|
||
+ dbgsection = (char *)kltypesection + section_size;
|
||
+ lp = (long *)dbgsection;
|
||
+ section_size = *lp++;
|
||
+ /* sanity check: */
|
||
+ if ((dbgsection + section_size) != ((char *)file_storage+file_size)) {
|
||
+ printk("dbg_sym_ts do not end at end of file\n");
|
||
+ goto bad;
|
||
+ }
|
||
+ num_dsyms = *lp++;
|
||
+ element_size = *lp++;
|
||
+ /* sanity check: */
|
||
+ if (element_size != sizeof(dbg_sym_t)) {
|
||
+ printk("kdb: size of dbg_sym_t does not match lkcd\'s\n");
|
||
+ goto bad;
|
||
+ }
|
||
+
|
||
+ /* two special words ahead of the structures themselves */
|
||
+ head_types_tree = *lp++;
|
||
+ head_typedefs_tree = *lp++;
|
||
+
|
||
+ dbgstorage = (void *)lp;
|
||
+ dsym_types_array = (dbg_sym_t *)dbgstorage;
|
||
+
|
||
+ /* return the heads of the two binary trees */
|
||
+ *type_treepp = dsym_types_array+head_types_tree;
|
||
+ *typedef_treepp = dsym_types_array+head_typedefs_tree;
|
||
+
|
||
+ /* translate the indices in our our array of kltype_t's to pointers */
|
||
+ /* (see write_kltype() for the fields that can be translated) */
|
||
+ klp = kltype_array;
|
||
+ for (i=0; i<num_kltypes; i++, klp++) {
|
||
+ if (index_to_char_ptr(&klp->kl_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; i<num_dsyms; i++, dbgp++) {
|
||
+ if (index_to_char_ptr(&dbgp->sym_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 <linux/module.h>
|
||
+#include <linux/init.h>
|
||
+#include <linux/mm.h>
|
||
+#include <linux/pagemap.h>
|
||
+#include <linux/fs.h>
|
||
+#include <linux/bio.h>
|
||
+#include <linux/buffer_head.h>
|
||
+#include <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+#include <linux/blkdev.h>
|
||
+#include <linux/ctype.h>
|
||
+#include <linux/slab.h>
|
||
+
|
||
+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, "<vaddr>", "Display page", 0);
|
||
+#endif
|
||
+ kdb_register("inode", kdbm_inode, "<vaddr>", "Display inode", 0);
|
||
+ kdb_register("sb", kdbm_sb, "<vaddr>", "Display super_block", 0);
|
||
+ kdb_register("bh", kdbm_buffers, "<buffer head address>", "Display buffer", 0);
|
||
+ kdb_register("bio", kdbm_bio, "<bio address>", "Display bio", 0);
|
||
+ kdb_register("inode_pages", kdbm_inode_pages, "<inode *>", "Display pages in an inode", 0);
|
||
+ kdb_register("req", kdbm_request, "<vaddr>", "dump request struct", 0);
|
||
+ kdb_register("rqueue", kdbm_rqueue, "<vaddr>", "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 <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+#include <linux/module.h>
|
||
+#include <linux/init.h>
|
||
+#include <linux/sched.h>
|
||
+
|
||
+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, "<cpunum>", "Display runqueue for <cpunum>", 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 <linux/blkdev.h>
|
||
+#include <linux/types.h>
|
||
+#include <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+#include <linux/module.h>
|
||
+#include <linux/init.h>
|
||
+#include <linux/mm.h>
|
||
+#include <linux/sched.h>
|
||
+#include <linux/slab.h>
|
||
+#include <asm/signal.h>
|
||
+
|
||
+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, "<vaddr>", "Display task_struct", 0);
|
||
+ kdb_register("sigset", kdbm_sigset, "<vaddr>", "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 <linux/blkdev.h>
|
||
+#include <linux/types.h>
|
||
+#include <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+#include <linux/module.h>
|
||
+#include <linux/init.h>
|
||
+#include <linux/mm.h>
|
||
+#include <linux/swap.h>
|
||
+#include <linux/swapops.h>
|
||
+#include <linux/slab.h>
|
||
+
|
||
+#include <scsi.h>
|
||
+#include <scsi/scsi_host.h>
|
||
+#include <asm/pgtable.h>
|
||
+
|
||
+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 <linux/mempolicy.h>
|
||
+
|
||
+/*
|
||
+ * kdbm_mpol
|
||
+ *
|
||
+ * This function implements the 'mempolicy' command.
|
||
+ * Print a struct mempolicy.
|
||
+ *
|
||
+ * mempolicy <address> Print struct mempolicy at <address>
|
||
+ */
|
||
+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 <node_id> Print struct pglist_data for node <node_id>.
|
||
+ *
|
||
+ * 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] <address> Print vm_area_struct at <address>
|
||
+ * vmp [-v] <pid> Print all vm_area_structs for <pid>
|
||
+ */
|
||
+
|
||
+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 (<address> through <address>
|
||
+ * plus <nbytes>) for the given process. The default value for nbytes is
|
||
+ * one.
|
||
+ *
|
||
+ * pte -m <mm> <address> [<nbytes>] Print all pte_t structures for
|
||
+ * virtual <address> in address space
|
||
+ * of <mm> which is a pointer to a
|
||
+ * mm_struct
|
||
+ * pte -p <pid> <address> [<nbytes>] Print all pte_t structures for
|
||
+ * virtual <address> in address space
|
||
+ * of <pid>
|
||
+ */
|
||
+
|
||
+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 (<pfn> through <pfn>
|
||
+ * plus <npages>) for the given process. The default value for npages is
|
||
+ * one.
|
||
+ *
|
||
+ * rpte -m <mm> <pfn> [<npages>] Print all pte_t structures for
|
||
+ * physical page <pfn> in address space
|
||
+ * of <mm> which is a pointer to a
|
||
+ * mm_struct
|
||
+ * rpte -p <pid> <pfn> [<npages>] Print all pte_t structures for
|
||
+ * physical page <pfn> in address space
|
||
+ * of <pid>
|
||
+ */
|
||
+
|
||
+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] <vaddr>", "Display vm_area_struct", 0);
|
||
+ kdb_register("vmp", kdbm_vm, "[-v] <pid>", "Display all vm_area_struct for <pid>", 0);
|
||
+#ifdef CONFIG_NUMA
|
||
+ kdb_register("mempolicy", kdbm_mpol, "<vaddr>", "Display mempolicy structure", 0);
|
||
+ kdb_register("pgdat", kdbm_pgdat, "<node_id>", "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 <mm> | -p <pid> ) <vaddr> [<nbytes>]", "Display pte_t for mm_struct or pid", 0);
|
||
+ kdb_register("rpte", kdbm_rpte, "( -m <mm> | -p <pid> ) <pfn> [<npages>]", "Find pte_t containing pfn for mm_struct or pid", 0);
|
||
+ kdb_register("dentry", kdbm_dentry, "<dentry>", "Display interesting dentry stuff", 0);
|
||
+ kdb_register("kobject", kdbm_kobject, "<kobject>", "Display interesting kobject stuff", 0);
|
||
+ kdb_register("filp", kdbm_filp, "<filp>", "Display interesting filp stuff", 0);
|
||
+ kdb_register("fl", kdbm_fl, "<fl>", "Display interesting file_lock stuff", 0);
|
||
+ kdb_register("sh", kdbm_sh, "<vaddr>", "Show scsi_host", 0);
|
||
+ kdb_register("sd", kdbm_sd, "<vaddr>", "Show scsi_device", 0);
|
||
+ kdb_register("sc", kdbm_sc, "<vaddr>", "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. <vamsi_krishna@in.ibm.com>
|
||
+ * (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 <linux/interrupt.h>
|
||
+#include <linux/types.h>
|
||
+#include <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+#include <linux/module.h>
|
||
+#include <linux/init.h>
|
||
+#include <linux/mm.h>
|
||
+
|
||
+#include <asm/processor.h>
|
||
+#include <asm/uaccess.h>
|
||
+#include <asm/desc.h>
|
||
+#include <asm/debugreg.h>
|
||
+#if 0
|
||
+#include <asm/pgtable.h>
|
||
+#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 [<selector> [<line count>]]
|
||
+ *
|
||
+ * 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 [<selector> [<line count>]]
|
||
+ *
|
||
+ * 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 [<vector> [<line count>]]
|
||
+ *
|
||
+ * 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 <addr arg> [<line count>]
|
||
+ *
|
||
+ * 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, "<sel> [<count>]", "Display GDT", 0, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("idt", kdb_idt, "<int> [<count>]", "Display IDT", 0, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("ldt", kdb_ldt, "<sel> [<count>]", "Display LDT", 0, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("ptex", kdb_pte, "<addr> [<count>]", "Display pagetables", 0, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register_repeat("ldtp", kdb_ldt, "<sel> [<count>]", "Display Process LDT", 0, KDB_REPEAT_NO_ARGS);
|
||
+ kdb_register("rdmsr", kdb_rdmsr, "<maddr>", "Display Model Specific Register", 0);
|
||
+ kdb_register("wrmsr", kdb_wrmsr, "<maddr> <h> <l>", "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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <kl_dump_ia64.h> */
|
||
+#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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <klib.h>
|
||
+#include <asm/ioctl.h>
|
||
+#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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <kl_mem_alpha.h>
|
||
+# include <kl_dump_alpha.h>
|
||
+#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 <kl_mem_i386.h>
|
||
+# include <kl_dump_i386.h>
|
||
+#endif
|
||
+#ifdef DUMP_ARCH_S390
|
||
+# include <kl_mem_s390.h>
|
||
+# include <kl_dump_s390.h>
|
||
+#endif
|
||
+#ifdef DUMP_ARCH_S390X
|
||
+# include <kl_mem_s390x.h>
|
||
+# include <kl_dump_s390.h>
|
||
+#endif
|
||
+#ifdef DUMP_ARCH_ARM
|
||
+# include <kl_mem_arm.h>
|
||
+# include <kl_dump_arm.h>
|
||
+#endif
|
||
+#ifdef DUMP_ARCH_PPC64
|
||
+#include <kl_mem_ppc64.h>
|
||
+#include <kl_dump_ppc64.h>
|
||
+#endif
|
||
+#ifdef DUMP_ARCH_X86_64
|
||
+#include <kl_mem_x86_64.h>
|
||
+#include <kl_dump_x86_64.h>
|
||
+#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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <stdio.h>
|
||
+#include <stdlib.h>
|
||
+#include <stdint.h>
|
||
+#include <unistd.h>
|
||
+#include <sys/stat.h>
|
||
+#include <sys/types.h>
|
||
+#include <sys/time.h>
|
||
+#include <unistd.h>
|
||
+#include <time.h>
|
||
+#include <sys/socket.h>
|
||
+#include <netinet/in.h>
|
||
+#include <netdb.h>
|
||
+#include <sys/ioctl.h>
|
||
+#include <net/if.h>
|
||
+#include <sys/utsname.h>
|
||
+#include <fcntl.h>
|
||
+#include <string.h>
|
||
+#include <setjmp.h>
|
||
+#include <strings.h>
|
||
+#include <errno.h>
|
||
+#include <assert.h>
|
||
+#include <ctype.h>
|
||
+#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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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<<PAGE_SHIFT
|
||
+ * and
|
||
+ * #if defined(CONFIG_IA64_PAGE_SIZE_4KB)
|
||
+ * # define PAGE_SHIFT 12
|
||
+ * #elif defined(CONFIG_IA64_PAGE_SIZE_8KB)
|
||
+ * # define PAGE_SHIFT 13
|
||
+ * #elif defined(CONFIG_IA64_PAGE_SIZE_16KB)
|
||
+ * # define PAGE_SHIFT 14
|
||
+ * #elif defined(CONFIG_IA64_PAGE_SIZE_64KB)
|
||
+ * # define PAGE_SHIFT 16
|
||
+ * #else
|
||
+ * # error Unsupported page size!
|
||
+ * #endif
|
||
+ * and
|
||
+ * #if defined(CONFIG_IA64_PAGE_SIZE_4KB)
|
||
+ * # define IA64_TASK_STRUCT_LOG_NUM_PAGES 3
|
||
+ * #elif defined(CONFIG_IA64_PAGE_SIZE_8KB)
|
||
+ * # define IA64_TASK_STRUCT_LOG_NUM_PAGES 2
|
||
+ * #elif defined(CONFIG_IA64_PAGE_SIZE_16KB)
|
||
+ * # define IA64_TASK_STRUCT_LOG_NUM_PAGES 1
|
||
+ * #else
|
||
+ * # define IA64_TASK_STRUCT_LOG_NUM_PAGES 0
|
||
+ * #endif
|
||
+ * Finally we have for page sizes 4KB, 8K, 16K IA64_STK_OFFSET=32K, and
|
||
+ * for page size 64K IA64_STK_OFFSET=64K.
|
||
+ * FIXME: !!!Don't know how to handle 64K page size case!!!
|
||
+ */
|
||
+#define KL_KSTACK_SIZE_IA64 0x8000ULL
|
||
+/* 64KB page size case:
|
||
+ * #define KL_KSTACK_SIZE_IA64 0x10000ULL
|
||
+ */
|
||
+
|
||
+#define KL_PAGE_OFFSET_IA64 0xe000000000000000
|
||
+
|
||
+#define KL_PAGE_SHIFT_IA64 KL_PAGE_SHIFT
|
||
+#define KL_PAGE_SIZE_IA64 (1ULL << KL_PAGE_SHIFT_IA64)
|
||
+#define KL_PAGE_MASK_IA64 (~(KL_PAGE_SIZE_IA64-1))
|
||
+
|
||
+/* for 3-level page tables: */
|
||
+#define KL_PGDIR_SHIFT_IA64 (KL_PAGE_SHIFT_IA64+(KL_PAGE_SHIFT_IA64-3)*2)
|
||
+#define KL_PGDIR_SIZE_IA64 (1ULL<<KL_PGDIR_SHIFT_IA64)
|
||
+#define KL_PGDIR_MASK_IA64 (~(KL_PGDIR_SIZE_IA64-1))
|
||
+
|
||
+/* for 4-level page tables: */
|
||
+#define KL_PGDIR4_SHIFT_IA64 (KL_PAGE_SHIFT_IA64+(KL_PAGE_SHIFT_IA64-3)*3)
|
||
+#define KL_PGDIR4_SIZE_IA64 (1ULL<<KL_PGDIR4_SHIFT_IA64)
|
||
+#define KL_PGDIR4_MASK_IA64 (~(KL_PGDIR_SIZE4_IA64-1))
|
||
+#define KL_PUD_SHIFT_IA64 (KL_PAGE_SHIFT_IA64+(KL_PAGE_SHIFT_IA64-3)*2)
|
||
+#define KL_PUD_SIZE_IA64 (1ULL<<KL_PUD_SHIFT_IA64)
|
||
+#define KL_PUD_MASK_IA64 (~(KL_PUD_SIZE_IA64-1))
|
||
+
|
||
+#define KL_PMD_SHIFT_IA64 (KL_PAGE_SHIFT_IA64+(KL_PAGE_SHIFT_IA64-3)*1)
|
||
+#define KL_PMD_SIZE_IA64 (1ULL<<KL_PMD_SHIFT_IA64)
|
||
+#define KL_PMD_MASK_IA64 (~(KL_PMD_SIZE_IA64-1))
|
||
+
|
||
+#define KL_PTRS_PER_PGD_IA64 (1ULL<<(KL_PAGE_SHIFT_IA64-3))
|
||
+#define KL_PTRS_PER_PUD_IA64 (1ULL<<(KL_PAGE_SHIFT_IA64-3))
|
||
+#define KL_PTRS_PER_PMD_IA64 (1ULL<<(KL_PAGE_SHIFT_IA64-3))
|
||
+#define KL_PTRS_PER_PTE_IA64 (1ULL<<(KL_PAGE_SHIFT_IA64-3))
|
||
+
|
||
+/* These values describe the bits of pgd/pmd/pte entries that are
|
||
+ * status bits and therefor have to be masked in order to get valid
|
||
+ * addresses
|
||
+ */
|
||
+#define KL_PMD_BASE_MASK_IA64 (((1ULL<<50)-1)&(~0xfffULL))
|
||
+#define KL_PT_BASE_MASK_IA64 KL_PMD_BASE_MASK_IA64
|
||
+#define KL_PAGE_BASE_MASK_IA64 KL_PMD_BASE_MASK_IA64
|
||
+
|
||
+#define KL_KADDR_IS_HIGHMEM(vaddr) ((KL_HIGH_MEMORY && (vaddr >= 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <asm/kl_types.h> */
|
||
+#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 <j-nomura@ce.jp.nec.com>
|
||
+ *
|
||
+ * 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 <stdio.h>
|
||
+#include <stdlib.h>
|
||
+#include <stdarg.h>
|
||
+#include <stdint.h>
|
||
+#include <unistd.h>
|
||
+#include <sys/stat.h>
|
||
+#include <unistd.h>
|
||
+#include <time.h>
|
||
+#include <sys/time.h>
|
||
+#include <sys/types.h>
|
||
+#include <fcntl.h>
|
||
+#include <string.h>
|
||
+#include <setjmp.h>
|
||
+#include <strings.h>
|
||
+#include <errno.h>
|
||
+#include <ctype.h>
|
||
+#include <bfd.h>
|
||
+#include <assert.h>
|
||
+#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 <linux/kdb.h>
|
||
+#endif
|
||
#include <linux/mm.h>
|
||
#include <linux/slab.h>
|
||
#include <linux/interrupt.h>
|
||
--- 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 <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+
|
||
+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 <asm/system.h>
|
||
#include <asm/sections.h>
|
||
|
||
+#ifdef CONFIG_KDB_KDUMP
|
||
+#include <linux/module.h>
|
||
+#include <linux/device.h>
|
||
+#include <linux/kdb.h>
|
||
+#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 <linux/kdb.h>
|
||
+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 <linux/init.h>
|
||
#include <linux/nmi.h>
|
||
#include <linux/dmi.h>
|
||
+#ifdef CONFIG_KDB_KDUMP
|
||
+#include <linux/kdb.h>
|
||
+#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 <linux/kdb.h>
|
||
+
|
||
+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; i<CPU_LOAD_IDX_MAX; i++)
|
||
+ xxx_printf(" %lu", rq->cpu_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 <linux/kdb.h>
|
||
+/*
|
||
+ * 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 <linux/bug.h>
|
||
#include <linux/sched.h>
|
||
|
||
+#ifdef CONFIG_KDB
|
||
+#include <linux/kdb.h>
|
||
+#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 <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+/* 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 <linux/swap.h>
|
||
#include <linux/vmalloc.h>
|
||
#include <linux/pagemap.h>
|
||
+#ifdef CONFIG_KDB
|
||
+#include <linux/kdb.h>
|
||
+#include <linux/kdbprivate.h>
|
||
+#endif /* CONFIG_KDB */
|
||
#include <linux/namei.h>
|
||
#include <linux/shm.h>
|
||
#include <linux/blkdev.h>
|
||
@@ -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.
|
||
*
|